Graphics: Images

In this section, you will learn about:

This section assumes you have read through the tutorial on how to use Image Reformer/JSFormer and have successfully imported an image without issues. If you are having issues with Image Reformer/JSFormer, read the tutorial again, then make a post on Piazza if your issue has still not been resolved.

As a review of the tutorial, the following function allows you to draw images:

To nobody's surprise, context is a pointer to the graphics context you are using. pBitmap is a pointer to the tImage or Graphics_Image you want to use. This can be found near the bottom of the .c file you created for your imported image. Here is an example used in the demo for this section:

For this example, you would want a pointer to MarioBrosTile8BPP_UNCOMP. To get this pointer, go to the file where you want to call Graphics_drawImage. At the top of the file, declare an extern const Graphics_Image with the name of the tImage you want to use. This would look something like this:

extern const Graphics_Image MarioBrosTile8PP_UNCOMP;

Finally, x and y are the coordinates for the top-left corner of the image. Do not confuse this for the center of the image! Because the function takes the coordinates of the top-left corner, drawing an image at x = 127 or y = 127 will appear to have no effect, since the image is being "drawn" off-screen!

To summarize, this is an example of how an image can be drawn with its top-left corner at the center of the LCD (using the Graphics_Image declaration above):

Graphics_Context g_sContext;


Graphics_drawImage(&g_sContext, &MarioBrosTile8PP_UNCOMP, 63, 63);

You may be thinking that images are more complicated to erase than circles, rectangles, or lines because of their irregular shape. In actuality, this is not the case, and the matter of erasing images is rather simple. Transparency is the reason why an image might seem to have an irregular shape. However, when using Image Reformer, transparency is eliminated (and images with transparency have been known to not convert properly using Image Reformer). Since none of the images imported into CCS have transparency, the images must have some sort of defined shape. In this case, the shape is a rectangle.

So what's the solution to erasing an image? Make a Graphics_Rectangle that's the same size as your image. Keep track of the image's position, then simply draw the Graphics_Rectangle with the background color to erase the image! But how do you know how large your image is? You will need to keep track of this before you import an image. Let's use the Mario Bros. Tile example used above.

Make note of the width and height dimensions you've set for your image on the right. This is the size of your converted image in pixels. Make sure you are using the dimensions on the right; the dimensions on the left are for the original image! In this particular example, the image used was already 16x16 pixels, so the dimensions on the left and right happen to match up. However, this will not always be the case, so take care not to mix up the two dimensions!

In any case, to erase this tile sprite wherever it is printed on the board, we now know enough information to make a Graphics_Rectangle that fits the size of the image.

The reason we use minus one for xMax and yMax is because the boundaries of the rectangle are inclusive. For example, say we have an image that is 4 pixels wide. If we set our xMin to 0 and set xMax to 4, the resulting rectangle would be 5 pixels wide. Why? Because there are pixels at x = 0, x = 1, x = 2, x = 3, and x = 4 (for a total of five pixels included in the rectangle). Thus, reducing xMax by 1 (thus making it equal to 3) would result in a rectangle that is 4 pixels wide, just like the image. 

Let's show this in an example. Assume the foreground color is white and the background color is black. First, let's draw an image (or at least its corner) at the center of the screen. We will use the same Mario Bros. Tile image used in the previous examples for this section.

Graphics_Context g_sContext;


unsigned x = 63, y = 63;

Graphics_drawImage(&g_sContext, &MarioBrosTile8PP_UNCOMP, x, y);

Now, let's erase the image and move it somewhere else. Create a rectangle, then draw it over the old image (which is 16x16 pixels).

Graphics_Rectangle eraseBox = {x, y, x+16-1, y+16-1};

Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_BLACK);

Graphics_fillRectangle(&g_sContext, &eraseBox);

Now, let's redraw the image 8 pixels to the left and up.

x -= 8;

y -= 8;

Graphics_drawImage(&g_sContext, &MarioBrosTile8PP_UNCOMP, x, y);

Drawing with images can get confusing at times. As such, you should try to make your code as easy to read as possible. Let's use the demo below as an example for how to make easy-to-read code when working with images. The demo shows a simple scene of Mario moving across a layer of tiles. Mario moves from one side of the screen to the other at a rate of 4 pixels every 0.25 seconds (or 16 pixels per second).

https://github.com/ECE2564-VT/LCD_imageDemo

Usually, the best way to add clarity is by using macros. SPRITE_OFFSET was used to represent both the height and width of every image used in this demo (all of which were 16x16). Since every image used in this demo was identical in size, only one macro was defined. If you are using multiple images of varying heights and widths, you should define height and width macros for each individual image.

Additionally, having to use long names like MarioBrosTile8BPP_UNCOMP can be tiresome and difficult to read. Consider using macros to change those instances into readable names. In the case of the demo, TILE_SPRITE was used in place of MarioBrosTile8BPP_UNCOMP, and MARIO_SPRITE was used in place of Mario8BPP_UNCOMP.

If you have an idea for how many pixels you want your image to move every time the timer expires, consider making macros for those increments. In this case, MOVE_INCREMENT was defined for this purpose. Not only does this macro make it clear which parts of the code deal with updating Mario's position, but it also makes it simple to adjust the speed. All you have to do is change the number in the macro definition to change the amount of pixels Mario moves per timer expiration!