Paletted Textures
Specifying a palette
The PVR graphics chip has an internal bank of color palette entries. This bank contains a total of 1024 color entries, each of which is 32 bits wide (no matter what truecolor format you choose).
The color palette can be configured in three modes which match the format of the standard 16-bit textures: ARGB1555, ARGB4444, RGB565. There's also a 32-bit mode, which allows you to use ARGB8888 colour palette entries.
The palette mode is a global setting - it affects all rendering of paletted textures. So it's a good idea to set this to something that fills all your paletted texture needs.
To set the palette format, you use the pvr_set_pal_format function, with one of the PVR_PAL_ values. For example, to set up an ARGB8888 palette:
pvr_set_pal_format(PVR_PAL_ARGB8888);
To set a colour palette entry, use the pvr_set_pal_entry function. This function takes a palette index from 0 to 1023, and a colour value. The colour value is in whatever format you set the palette to. In our case, that's ARGB8888. So, to set the first four palette entries to transparent, black, white, and red, we could do something like this:
#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) ) pvr_set_pal_entry(0, PACK_ARGB8888(0, 0, 0, 0)); pvr_set_pal_entry(1, PACK_ARGB8888(255, 0, 0, 0)); pvr_set_pal_entry(2, PACK_ARGB8888(255, 255, 255, 255)); pvr_set_pal_entry(3, PACK_ARGB8888(255, 255, 0, 0));
There are 1024 colour palette entries. If you're using 4bpp images (16 colours each), you treat this as 64 separate 16 colour palettes. Palette 0 consists of colours 0 through 15, palette 1 consists of colours 16 through 31, and so on. For 8bpp images, you get four 256 colour palettes, with palette 0 being colours 0 through 255, palette 1 being colours 256 through 511, and so on.
We'll cover how to select which palette to use later. If you do not set a palette, you end up with palette 0.
Loading textures
You can load a paletted texture in the normal way, using pvr_txr_load_ex. You just need to make sure they're twiddled, which pvr_txr_load_ex will do for you.
Actually loading texture data is a bit trickier than it could be - the utility functions in KOS to load textures automatically convert everything to 16bpp RGB565, which is obviously not what we want.
I might post some sample code for reading a paletted image later. Most likely, either a PNG, PCX, or BMP file. But for now, just assume that you've managed to load an 8bpp image from somewhere...
void *image_data; pvr_ptr_t texture_pointer; int width, height;
To load an 8bpp image...
pvr_txr_load_ex(image_data, texture_pointer, width, height, PVR_TXRLOAD_8BPP);
To load a 4bpp image...
pvr_txr_load_ex(image_data, texture_pointer, width, height, PVR_TXRLOAD_4BPP);
Using paletted textures
Now that we've come this far, we get to the easy part - using the textures.
To use a paletted texture, you just need to specify the texture format when building the polygon header. For example, for an 8-bit texture with no filtering, using the default palette, and being rendered into the punch-through display list:
pvr_poly_hdr_t hdr; pvr_poly_cxt_t cxt; pvr_poly_cxt_txr(&cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_PAL8BPP, width, height, texture_pointer, PVR_FILTER_NONE); pvr_poly_compile(&hdr, &cxt);
Same deal for 4bpp textures - just substitude PVR_TXRFMT_PAL4BPP.
Choosing a different palette
If you want to use something other than the default palette, you need to use the PVR_TXRFMT_8BPP_PAL and PVR_TXRFMT_4BPP_PAL macros:
pvr_poly_cxt_txr(&cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_PAL8BPP | PVR_TXRFMT_8BPP_PAL(1), width, height, texture_pointer, PVR_FILTER_NONE);
The numbers passed to PVR_TXRFMT_xBPP_PAL are palette numbers, not indexes. So in this example, the texture will use the second 256 colour palette, consisting of palette entries 256 through 511.
Why bother?
The main reason is memory usage. An 8bpp image takes up half as much video RAM as a 16bpp image, and a 4bpp image takes up half as much again. This is useful for 2D games, particularly if the source images use colour palettes anyway.
You also get the ability to do palette animation, and use the same image multiple times with different colour palettes. These are tricks that a lot of old console games used to use to get animation from static images, and to provide a bit of variety to the graphics without needing to include more graphics data.
You can also use paletted images for fonts. The ROM fonts are natively 4bpp images. Typically, we would convert them to a 16bpp ARGB4444 image, with the colour of every pixel set to white. That's a huge waste of memory, when we could just load the font as a 4bpp texture. In fact, anywhere you might use an ARGB4444 texture with all the colours set to white can be replaced with a 4bpp texture.
It's also useful for simulating luminance-only and alpha-only textures. You can simulate a luminance-only (greyscale) texture by using an 8bpp image, and setting colour palette entry i to ARGB(255, i, i, i). Similarly, you can simulate an alpha-only texture by setting colour palette entry i to ARGB(i, 255, 255, 255). By itself, this isn't necessarily useful, since the PVR's framebuffer is only 5 or 6 bits per colour channel. However, if you combine this with blending, you can eliminate a lot of the rendering artefacts you'd get by using an ARGB4444 texture. For example, you might have a particle texture which is coloured by setting the vertex colours, and blended onto the scene, or a lightmap. Since the PVR renders everything internally in 32bpp, and only converts to RGB565 as the final step, rendering quality would be improved.