Graphics Palettes

From TLoZ: ALTTP Hacking Resources
Jump to: navigation, search

See also: Standard Notation

Outside of Mode 7, the SNES uses indexed graphics: Instead of storing the RGB color value individually for each pixel as modern computers do, each pixel's value is an index into an array of colors called a palette. (Color index 0 is always transparent, no matter which palette is in use.) This allows easy swapping of colors without having to change the underlying graphics (allowing sprite/object reuse), but also makes it possible to use the full range of the SNES's 15-bit color display without requiring two full bytes for every single pixel, which would quickly overwhelm the system's available working memory and would at least double the amount of time spent transferring bytes between the software and hardware.

The SNES supports different palette sizes depending on the graphics mode, such as one 256-color palette or 16 16-color palettes (8 for sprites and 8 for backgrounds). The color index 0 in each palette is always transparent, no matter how many or few palettes are in use. Most things in ALTTP are designed with 4-, 8-, or 16-color palettes which are adjusted at load time to use actual SNES palettes.

Bits Per Pixel

"Bits per pixel" refers to how many bits are used to hold the color index in each pixel of an image, which reflects the size of its palette.

2bpp
4-color palette (color indices $0..$3)
Often used for simple graphics like the HUD.
3bpp
8-color palette (color indices $0..$7)
Used for the vast majority of graphics in the game.
Note that this is a "fake" format used for compression by ALTTP, not supported by the SNES natively, and it is expanded to 4bpp at load time.
4bpp
16-color palette (color indices $0-$F)
Used for more intricate sprites like the player himself.

Other indexed formats are supported by the SNES, but are not used by ALTTP.

SNES and Modern Color Representation

Most modern computing devices use 24-bit color: 8 bits for red, 8 bits for green, and 8 bits for blue, for a total of 16777216 possible colors. The SNES uses 15-bit color: 5 bits for red, 5 bits for green, and 5 bits for blue, for a total of 32768 colors. Both of these representations encode the same thing with different resolution: 0%-100% of the red, green, and blue components of the color.

There are two ways to convert between SNES and modern representation: Bitwise and ratiowise.

Bitwise Conversion

This method requires no floating-point calculation. Since an 8-bit value can hold 256 different values and a 5-bit value can hold 32 different values, and since 256 / 32 == 8, the simplest possible way to convert from modern to SNES is to divide by 8 (or shift right by 3) and from SNES to modern is to multiply by 8 (or shift left by 3).

SNES_color_value = modern_color_value >> 3
modern_color_value = SNES_color_value << 3

The obvious problem with this method is the loss of precision: A round-trip conversion from modern to SNES to modern cuts the color values to a maximum of 248 each, causing all colors to be dimmer and "pure white" to be a light grey. However, this does actually mimic real SNES hardware, in that real (original) SNES (and SFC) hardware is dim in this way. So, for the closest representation to the majority of real SNES hardware, this method should be used.

Ratiowise Conversion

This method is more intuitive and more proper mathematically, more closely matches the color levels produced by the Super Famicom Jr., and can be done with both integer and floating-point computation. Put simply, you divide the color value by its current maximum value (255 for modern and 31 for SNES) to convert it to a number from 0 to 1 (as in 0% to 100%), then multiply by its new maximum value (again 255 for modern and 31 for SNES).

SNES_color_value = ( modern_color_value * 31 ) / 255
modern_color_value = ( SNES_color_value * 255 ) / 31

The reason the multiplication is done before the division is that integer division discards the remainder, so doing the multiplication first results in the least loss of precision. A better way to do things is to use floating-point computation.

SNES_color_value = round_to_nearest_integer ( modern_color_value / 255.0 * 31.0 )
modern_color_value = round_to_nearest_integer ( SNES_color_value / 31.0 * 255.0 )

This method produces the least possible loss of precision, meaning the most accurate possible round-trip conversion when working with SNES graphics in modern graphics programs. Converting SNES to modern color values in this way is less accurate to the original SNES/SFC hardware, but the difference is minor and only some emulators display brightness levels matching original SNES/SFC hardware anyway, so this method is actually more accurate when targetting such emulators or the SFC Jr. hardware.

Palette Format

Each 15-bit color in a palette is stored packed in two bytes. Let (R,G,B) be the three 5-bit components of the color value, and let (C) be the final packed color value as part of the palette.

C = ( B << 10 ) | ( G << 5 ) | R
R = C & $1F
G = ( C >> 5 ) & $1F
B = C >> 10

Note that the highest bit (bit index 15) of the palette entry is not used, and should be set to 0.