Using Images

Overview

The GBA does not have an operating system, so it does not have the ability to load files, because the file system is part of the operating system. This means that all data used by your program has to be put into the source code.

The programs we've done so far have used plain colored shapes, but most games use images. Today we will see how to put image data into a GBA game.

Image Data

As we have seen, colors on the GBA are stored in a 15-bit BGR format. Different image formats and systems represent colors differently. In order to use an image in a GBA program, we'll need to ensure the data is in the correct format, and also somehow import it directly into our code.

The "png2gba" program which is installed on cs at /home/finlayson/305/files/png2gba can be used to convert PNG images into data usable on the GBA. This program is installed on the lab computers and the 305 virtual machine. You can download the code yourself here.

This reads in a PNG image file, which typically stores data in a compressed, 24-bit per pixel format. It converts each pixel into the 15-bit BGR format, and writes the raw values into a C array.

For example, this image:

Can be converted to the GBA format using the command:

png2gba image.png

Which produces the output image.h.

This file could then be #included into a source file to be used.

An Image Example

This example displays an image on the screen.

It first includes the image data file at the top:


/* include the actual image file data */
#include "image.h"


This example uses mode 3, so it simply sets the mode, then loops through all 240 by 160 pixels, and copies from the image array into the screen:


/* we set the mode to mode 3 with bg2 on */
*display_control = MODE3 | BG2;

/* loop through each column of the screen */
for (int row = 0; row < HEIGHT; row++) {
for (int col = 0; col < WIDTH; col++) {
put_pixel(row, col, image_data[row * WIDTH + col]);
}
}


The image_data variable is the large array defined in the image.h data file.

Using the Palette

This works well enough, except that mode 3 does not really allow for interactive graphics due to the lack of a second buffer. We can use mode 4 to get the second buffer, but then we need to use the color palette.

This means our image data would not be an array of 16-bit values, it would be an array of 8-bit entries into a 256-entry color palette, which would also need to be specified somehow.

The png2gba program is capable of producing image data for use with a palette. To do so, pass it the -p option:

png2gba -p image.png

This produces an image data file image.h.

This produces two arrays: the palette and the actual image data.

Mode 4 Images

This example demonstrates displaying an image in mode 4. The main function starts by setting the display control register to mode 4, and then copying the image palette into the palette memory:


/* we set the mode to mode 4 with bg2 on */
*display_control = MODE4 | BG2;

/* load the palette into palette memory */
for (int i = 0; i < PALETTE_SIZE; i++) {
palette[i] = image_palette[i];
}


We then copy the image into screen memory. In mode 4, the screen stores 8-bit palette indices, but recall that we must write it 16-bits at a time. In our last example, we wrote to the screen pixel by pixel, but patched the pixels into 16-bit values.

If we have many pixels to write, it is more efficient to write an entire 16-bit value, representing two pixels at one time. This is what the following function does:


/* write an image into a buffer */
void draw_image(volatile unsigned short* buffer, const unsigned char* data) {
/* copy the data 16 bits at a time */
unsigned short* data_wide = (unsigned short*) data;

for (int i = 0; i < (WIDTH * HEIGHT) / 2; i++) {
buffer[i] = data_wide[i];

}
}

In order to copy the 8-bit values 16-bits at a time, we create a pointer to an unsigned short, and then point it at the 8-bit memory. Notice we then only have to loop through half the screen.

Images in Bitmap Modes

Using images to write games in one of the bitmap modes will entail:

• Creating in image in some graphics editor.
• Converting the image into a C array of pixel data.
• Reading from the image into some portion of the screen.

Because there is only one palette, a common practice is to put all of the images used at once into the same image. Then we can reference different portions of the image for different objects.

Looking Forward

Most of the concepts we have seen so far in GBA programs are relevant for all modes:

• Image and screen memory.
• Double buffering.
• Palettes.
• The video blank cycle.
• Hardware registers and input.

However, the bitmap modes are not widely used for games because there are just not enough CPU cycles to create fast games that update a large portion of the screen every frame.

Next, we will learn the tile-based modes which use special hardware support for displaying tiled backgrounds quickly. We will also look at using hardware sprites which allow fast moveable objects.

Copyright © 2022 Ian Finlayson | Licensed under a Attribution-NonCommercial 4.0 International License.