The N64 JPEG compression method is based on the standard JPEG process. See the JPEG standard for a complete description of the exact process. Following is a brief explanation of the N64 JPEG compression process. The procedure for decompressing compressed data is basically the opposite of this.
The N64 compression process first converts data from 16-bit RGBA format to YUV format. During this process, less essential visual data (data that is less important in human vision) is culled. The resulting YUV data format is easier to compress.
First the data is converted from RGB to YCrCb format. The Y value holds the luminance (brightness) information and Cr and Cb values hold the chrominance (color) information:
r = R/255, g = G/255, b = B/255 (0<R<255, 0<G<255, 0<B<255) (0<r<1, 0<g<1, 0<b<1) Y = 0.299r + 0.587g + 0.114b Cr = 0.713(r - Y) Cb = 0.564(b - Y) (0<Y<1, -0.5<Cr<0.5, -0.5<Cb<0.5)
Then the YCrCb formatted data is converted to the YUV format:
y = 219Y + 16 u = 224Cb + 128 v = 224Cr + 128 (0<y<255, 0<u<255, 0<v<255)
Human eyes are relatively insensitive to UV components compared to the Y component. Thus, culling the UV components can decrease the amount of data without a large noticeable change. This process extracts four 8x8 blocks of the Y component and one 8x8 block of each UV (U and V) component from the 16x16 dots (macro block).
DCT (discrete cosine transformation) regulates the apparently random image to ease the compression process.
When processing an 8x8 matrix, DCT calculates an average value (the DC coefficient) of the data elements in the matrix and places it in the [0][0] position of the transformed matrix. Then it calculates the variance (the AC coefficient) from that DC coefficient for each of the other 63 cells of the 8x8 matrix. The farther the AC element is from the position of the DC element, the higher the displayed frequency element is.
The range of data after DCT is wide, and the wider the range the less compression, so you need to reduce the range. To reduce the range, divide each value by a specific value. Human vision is less sensitive to high frequency components than it is to low frequency components, so you should divide high frequency components by a large value and divide low frequency components by a small value.
The DCT and quantization processes concentrate on the DC components and low frequency components. The high frequency components don't change much and the data concentrates around 0. That is, large changes in data concentrate around cell [0][0] of the 8x8 pixel matrix after DCT, and many zeros concentrate around cell[7][7]. The continuous appearance of these zeros is profitable for compression, so this step rearranges the data to take advantage of the large concentration of zeros. One way to do this is to apply zigzag scanning to the data from [0][0] to [8][8] like this: [0][0] --> [1][0] --> [0][1] --> [0][2] --> [1][1] --> [2][1]... and so on.
DC components have differential data of DC components in the previous block so that the range of data values becomes smaller.
Note: The N64 JPEG process does the scanning after first inverting the matrix. This is different from normal JPEG zigzag scanning. It is done this way to ease microcode decompression of compressed data.
The previous five steps converted the random image into a compressible form. Now this final step completes the Huffman entropy encoding to compress the image data.
njpgMakeSlideThe output data formats are described below.
When the output data from the tool is to be embeded with the game application, first DMA-transfer numImages of images onto the memory, and then DMA-transfer frame[] according to numImages after checking the necessary information. If image data is necessary, it should be DMA-transfered onto the memory as you reference the data pointed to by frame[].
typedef struct{ s32 size; s32 offset; } MVFrame; struct{ u8 headerID[] = 'NJPG' s32 version; s16 width; s16 height; u8 rawMode; u8 compressType; s16 qScale; s32 numImages; MVFrame frame[]; u8 data[]; };
The data output by the njpgCompress tool is suited to only a part of the data output by the njpgMakeSlide tool. Be careful that the special supplemented header is not changed. Note that the minimum header required for data decompressing has been supplemented. The following is the data format. Refer to it to check the data when decompressing.
struct{ u8 headerID[] = "HUFF"; s16 numMB; /* number of macro blocks */ u8 compData[]; };
The quantization table for both luminance (brightness) and chroma (color) is adapted to the luminosity quantization table of quantization table in the attachment K(reference) "Examples and guide line" of ITU-T advice, T.81 "The digital compression and coding of continuous-tone still images." The quantization table has been organized to make RSP calculation faster when decompressing the compressed data.
The table follows.
static short QTable[64] = { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 };
As a practical matter, this table is used by multiples of 1/4, 1/2, and 2 (these correspond to the scale values of -2, -1, 1, and 2). When the value of the quantization table is small, the image quality goes up but the size after compression is larger. On the other hand, when the value of the quantization table is large, the size after the compression is small but the image quality is worse.
The Huffman encoding table is the one in the attachment K (reference) "Examples and Guidelines" of the ITU-T advice, T.81 "Digital compression and encoding of continuous-tone still images"
These tables follow.
Normally with JPEG compression process using the Huffman encoding to output the data, when an identical code (0xFF) with the marker appers in the data, the code should be followed by "0x00" by inserting it to indicate that the code is not a marker. This is because an ordinary data should be distinguished from a marker that defines information regarding the image. However, this process is not included because in N64 JPEG doesn't use a marker.
Table of DC Codes for Luminance (Brightness)
Classification | Code Length | Code Word |
---|---|---|
0 | 2 | 0x0000 |
1 | 3 | 0x0002 |
2 | 3 | 0x0003 |
3 | 3 | 0x0004 |
4 | 3 | 0x0005 |
5 | 3 | 0x0006 |
6 | 4 | 0x000e |
7 | 5 | 0x001e |
8 | 6 | 0x003e |
9 | 7 | 0x007e |
10 | 8 | 0x00fe |
11 | 9 | 0x01fe |
Table of DC Codes for Chroma (Color)
Classification | Code Length | Code Word |
---|---|---|
0 | 2 | 0x0000 |
1 | 2 | 0x0001 |
2 | 2 | 0x0002 |
3 | 3 | 0x0006 |
4 | 4 | 0x000e |
5 | 5 | 0x001e |
6 | 6 | 0x003e |
7 | 7 | 0x007e |
8 | 8 | 0x00fe |
9 | 9 | 0x01fe |
10 | 10 | 0x03fe |
11 | 11 | 0x07fe |
Table of AC Codes for Luminance (Brightness)
Run/Size | Code Length | Code Word |
---|---|---|
0/0(EOB) | 4 | 0x000a |
0/1 | 2 | 0x0000 |
0/2 | 2 | 0x0001 |
0/3 | 3 | 0x0004 |
0/4 | 4 | 0x000b |
0/5 | 5 | 0x001a |
0/6 | 7 | 0x0078 |
0/7 | 8 | 0x00f8 |
0/8 | 10 | 0x03f6 |
0/9 | 16 | 0xff82 |
0/A | 16 | 0xff83 |
1/1 | 4 | 0x000c |
1/2 | 5 | 0x001b |
1/3 | 7 | 0x0079 |
1/4 | 9 | 0x01f6 |
1/5 | 11 | 0x07f6 |
1/6 | 16 | 0xff84 |
1/7 | 16 | 0xff85 |
1/8 | 16 | 0xff86 |
1/9 | 16 | 0xff87 |
1/A | 16 | 0xff88 |
2/1 | 5 | 0x001c |
2/2 | 8 | 0x00f9 |
2/3 | 10 | 0x03f7 |
2/4 | 12 | 0x0ff4 |
2/5 | 16 | 0xff89 |
2/6 | 16 | 0xff8a |
2/7 | 16 | 0xff8b |
2/8 | 16 | 0xff8c |
2/9 | 16 | 0xff8d |
2/A | 16 | 0xff8e |
3/1 | 6 | 0x003a |
3/2 | 9 | 0x01f7 |
3/3 | 12 | 0x0ff5 |
3/4 | 16 | 0xff8f |
3/5 | 16 | 0xff90 |
3/6 | 16 | 0xff91 |
3/7 | 16 | 0xff92 |
3/8 | 16 | 0xff93 |
3/9 | 16 | 0xff94 |
3/A | 16 | 0xff95 |
4/1 | 6 | 0x003b |
4/2 | 10 | 0x03f8 |
4/3 | 16 | 0xff96 |
4/4 | 16 | 0xff97 |
4/5 | 16 | 0xff98 |
4/6 | 16 | 0xff99 |
4/7 | 16 | 0xff9a |
4/8 | 16 | 0xff9b |
4/9 | 16 | 0xff9c |
4/A | 16 | 0xff9d |
5/1 | 7 | 0x007a |
5/2 | 11 | 0x07f7 |
5/3 | 16 | 0xff9e |
5/4 | 16 | 0xff9f |
5/5 | 16 | 0xffa0 |
5/6 | 16 | 0xffa1 |
5/7 | 16 | 0xffa2 |
5/8 | 16 | 0xffa3 |
5/9 | 16 | 0xffa4 |
5/A | 16 | 0xffa5 |
6/1 | 7 | 0x007b |
6/2 | 12 | 0x0ff6 |
6/3 | 16 | 0xffa6 |
6/4 | 16 | 0xffa7 |
6/5 | 16 | 0xffa8 |
6/6 | 16 | 0xffa9 |
6/7 | 16 | 0xffaa |
6/8 | 16 | 0xffab |
6/9 | 16 | 0xffac |
6/A | 16 | 0xffad |
7/1 | 8 | 0x00fa |
7/2 | 12 | 0x0ff7 |
7/3 | 16 | 0xffae |
7/4 | 16 | 0xffaf |
7/5 | 16 | 0xffb0 |
7/6 | 16 | 0xffb1 |
7/7 | 16 | 0xffb2 |
7/8 | 16 | 0xffb3 |
7/9 | 16 | 0xffb4 |
7/A | 16 | 0xffb5 |
8/1 | 9 | 0x01f8 |
8/2 | 15 | 0x7fc0 |
8/3 | 16 | 0xffb6 |
8/4 | 16 | 0xffb7 |
8/5 | 16 | 0xffb8 |
8/6 | 16 | 0xffb9 |
8/7 | 16 | 0xffba |
8/8 | 16 | 0xffbb |
8/9 | 16 | 0xffbc |
8/A | 16 | 0xffbd |
9/1 | 9 | 0x01f9 |
9/2 | 16 | 0xffbe |
9/3 | 16 | 0xffbf |
9/4 | 16 | 0xffc0 |
9/5 | 16 | 0xffc1 |
9/6 | 16 | 0xffc2 |
9/7 | 16 | 0xffc3 |
9/8 | 16 | 0xffc4 |
9/9 | 16 | 0xffc5 |
9/A | 16 | 0xffc6 |
A/1 | 9 | 0x01fa |
A/2 | 16 | 0xffc7 |
A/3 | 16 | 0xffc8 |
A/4 | 16 | 0xffc9 |
A/5 | 16 | 0xffca |
A/6 | 16 | 0xffcb |
A/7 | 16 | 0xffcc |
A/8 | 16 | 0xffcd |
A/9 | 16 | 0xffce |
A/A | 16 | 0xffcf |
B/1 | 10 | 0x03f9 |
B/2 | 16 | 0xffd0 |
B/3 | 16 | 0xffd1 |
B/4 | 16 | 0xffd2 |
B/5 | 16 | 0xffd3 |
B/6 | 16 | 0xffd4 |
B/7 | 16 | 0xffd5 |
B/8 | 16 | 0xffd6 |
B/9 | 16 | 0xffd7 |
B/A | 16 | 0xffd8 |
C/1 | 10 | 0x03fa |
C/2 | 16 | 0xffd9 |
C/3 | 16 | 0xffda |
C/4 | 16 | 0xffdb |
C/5 | 16 | 0xffdc |
C/6 | 16 | 0xffdd |
C/7 | 16 | 0xffde |
C/8 | 16 | 0xffdf |
C/9 | 16 | 0xffe0 |
C/A | 16 | 0xffe1 |
D/1 | 11 | 0x07f8 |
D/2 | 16 | 0xffe2 |
D/3 | 16 | 0xffe3 |
D/4 | 16 | 0xffe4 |
D/5 | 16 | 0xffe5 |
D/6 | 16 | 0xffe6 |
D/7 | 16 | 0xffe7 |
D/8 | 16 | 0xffe8 |
D/9 | 16 | 0xffe9 |
D/A | 16 | 0xffea |
E/1 | 16 | 0xffeb |
E/2 | 16 | 0xffec |
E/3 | 16 | 0xffed |
E/4 | 16 | 0xffee |
E/5 | 16 | 0xffef |
E/6 | 16 | 0xfff0 |
E/7 | 16 | 0xfff1 |
E/8 | 16 | 0xfff2 |
E/9 | 16 | 0xfff3 |
E/A | 16 | 0xfff4 |
F/0(ZRL) | 11 | 0x07f9 |
F/1 | 16 | 0xfff5 |
F/2 | 16 | 0xfff6 |
F/3 | 16 | 0xfff7 |
F/4 | 16 | 0xfff8 |
F/5 | 16 | 0xfff9 |
F/6 | 16 | 0xfffa |
F/7 | 16 | 0xfffb |
F/8 | 16 | 0xfffc |
F/9 | 16 | 0xfffd |
F/A | 16 | 0xfffe |
Table of AC Codes for Chroma (Color)
Run/Size | Code Length | Code Word |
0/0(EOB) | 2 | 0x0000 |
0/1 | 2 | 0x0001 |
0/2 | 3 | 0x0004 |
0/3 | 4 | 0x000a |
0/4 | 5 | 0x0018 |
0/5 | 5 | 0x0019 |
0/6 | 6 | 0x0038 |
0/7 | 7 | 0x0078 |
0/8 | 9 | 0x01f4 |
0/9 | 10 | 0x03f6 |
0/A | 12 | 0x0ff4 |
1/1 | 4 | 0x000b |
1/2 | 6 | 0x0039 |
1/3 | 8 | 0x00f6 |
1/4 | 9 | 0x01f5 |
1/5 | 11 | 0x07f6 |
1/6 | 12 | 0x0ff5 |
1/7 | 16 | 0xff88 |
1/8 | 16 | 0xff89 |
1/9 | 16 | 0xff8a |
1/A | 16 | 0xff8b |
2/1 | 5 | 0x001a |
2/2 | 8 | 0x00f7 |
2/3 | 10 | 0x03f7 |
2/4 | 12 | 0x0ff6 |
2/5 | 15 | 0x7fc2 |
2/6 | 16 | 0xff8c |
2/7 | 16 | 0xff8d |
2/8 | 16 | 0xff8e |
2/9 | 16 | 0xff8f |
2/A | 16 | 0xff90 |
3/1 | 5 | 0x001b |
3/2 | 8 | 0x00f8 |
3/3 | 10 | 0x03f8 |
3/4 | 12 | 0x0ff7 |
3/5 | 16 | 0xff91 |
3/6 | 16 | 0xff92 |
3/7 | 16 | 0xff93 |
3/8 | 16 | 0xff94 |
3/9 | 16 | 0xff95 |
3/A | 16 | 0xff96 |
4/1 | 6 | 0x003a |
4/2 | 9 | 0x01f6 |
4/3 | 16 | 0xff97 |
4/4 | 16 | 0xff98 |
4/5 | 16 | 0xff99 |
4/6 | 16 | 0xff9a |
4/7 | 16 | 0xff9b |
4/8 | 16 | 0xff9c |
4/9 | 16 | 0xff9d |
4/A | 16 | 0xff9e |
5/1 | 16 | 0x003b |
5/2 | 16 | 0x03f9 |
5/3 | 16 | 0xff9f |
5/4 | 16 | 0xffa0 |
5/5 | 16 | 0xffa1 |
5/6 | 16 | 0xffa2 |
5/7 | 16 | 0xffa3 |
5/8 | 16 | 0xffa4 |
5/9 | 16 | 0xffa5 |
5/A | 16 | 0xffa6 |
6/1 | 7 | 0x0079 |
6/2 | 11 | 0x07f7 |
6/3 | 16 | 0xffa7 |
6/4 | 16 | 0xffa8 |
6/5 | 16 | 0xffa9 |
6/6 | 16 | 0xffaa |
6/7 | 16 | 0xffab |
6/8 | 16 | 0xffac |
6/9 | 16 | 0xffad |
6/A | 16 | 0xffae |
7/1 | 7 | 0x007a |
7/2 | 11 | 0x07f8 |
7/3 | 16 | 0xffaf |
7/4 | 16 | 0xffb0 |
7/5 | 16 | 0xffb1 |
7/6 | 16 | 0xffb2 |
7/7 | 16 | 0xffb3 |
7/8 | 16 | 0xffb4 |
7/9 | 16 | 0xffb5 |
7/A | 16 | 0xffb6 |
8/1 | 8 | 0x00f9 |
8/2 | 16 | 0xffb7 |
8/3 | 16 | 0xffb8 |
8/4 | 16 | 0xffb9 |
8/5 | 16 | 0xffba |
8/6 | 16 | 0xffbb |
8/7 | 16 | 0xffbc |
8/8 | 16 | 0xffbd |
8/9 | 16 | 0xffbe |
8/A | 16 | 0xffbf |
9/1 | 9 | 0x01f7 |
9/2 | 16 | 0xffc0 |
9/3 | 16 | 0xffc1 |
9/4 | 16 | 0xffc2 |
9/5 | 16 | 0xffc3 |
9/6 | 16 | 0xffc4 |
9/7 | 16 | 0xffc5 |
9/8 | 16 | 0xffc6 |
9/9 | 16 | 0xffc7 |
9/A | 16 | 0xffc8 |
A/1 | 9 | 0x01f8 |
A/2 | 16 | 0xffc9 |
A/3 | 16 | 0xffca |
A/4 | 16 | 0xffcb |
A/5 | 16 | 0xffcc |
A/6 | 16 | 0xffcd |
A/7 | 16 | 0xffce |
A/8 | 16 | 0xffcf |
A/9 | 16 | 0xffd0 |
A/A | 16 | 0xffd1 |
B/1 | 9 | 0x01f9 |
B/2 | 16 | 0xffd2 |
B/3 | 16 | 0xffd3 |
B/4 | 16 | 0xffd4 |
B/5 | 16 | 0xffd5 |
B/6 | 16 | 0xffd6 |
B/7 | 16 | 0xffd7 |
B/8 | 16 | 0xffd8 |
B/9 | 16 | 0xffd9 |
B/A | 16 | 0xffda |
C/1 | 9 | 0x01fa |
C/2 | 16 | 0xffdb |
C/3 | 16 | 0xffdc |
C/4 | 16 | 0xffdd |
C/5 | 16 | 0xffde |
C/6 | 16 | 0xffdf |
C/7 | 16 | 0xffe0 |
C/8 | 16 | 0xffe1 |
C/9 | 16 | 0xffe2 |
C/A | 16 | 0xffe3 |
D/1 | 11 | 0x07f9 |
D/2 | 16 | 0xffe4 |
D/3 | 16 | 0xffe5 |
D/4 | 16 | 0xffe6 |
D/5 | 16 | 0xffe7 |
D/6 | 16 | 0xffe8 |
D/7 | 16 | 0xffe9 |
D/8 | 16 | 0xffea |
D/9 | 16 | 0xffeb |
D/A | 16 | 0xffec |
E/1 | 14 | 0x3fe0 |
E/2 | 16 | 0xffed |
E/3 | 16 | 0xffee |
E/4 | 16 | 0xffef |
E/5 | 16 | 0xfff0 |
E/6 | 16 | 0xfff1 |
E/7 | 16 | 0xfff2 |
E/8 | 16 | 0xfff3 |
E/9 | 16 | 0xfff4 |
E/A | 16 | 0xfff5 |
F/0(ZRL) | 10 | 0x03fa |
F/1 | 15 | 0x7fc3 |
F/2 | 16 | 0xfff6 |
F/3 | 16 | 0xfff7 |
F/4 | 16 | 0xfff8 |
F/5 | 16 | 0xfff9 |
F/6 | 16 | 0xfffa |
F/7 | 16 | 0xfffb |
F/8 | 16 | 0xfffc |
F/9 | 16 | 0xfffd |
F/A | 16 | 0xfffe |
N64 JPEG raises the compression rate by slightly reducing the image quality. Therefore, if you repeatedly compress and decompress the compressed image, image quality can become noticeably worse.
Also, the N64 JPEG process includes errors, so you must be particularly careful when culling the UV data in the YUV space. For example, if you make an N64 drawing tool for use over and over in the N64 JPEG library for the purpose of image storage and retrieval, image encoding and decoding are provided whenever you repeat the storage and retrieval process. This means that if the drawing is done in the RGBA space (in the regular N64 image process space), UV data in the macro block is culled again and again based on the drawing. In short, an application that repeatedly compresses and decompresses a compressed image provides data culling over and over which can cause a noticeable deterioration in image quality.
If you make such an application, you should store the regular RGBA data without using compression or store the compressed data (for example, by the run length encoding as in the compression used in a BMP file), and use N64 JPEG as the final storage method.
Data decoded by the njpgdspMain microcode is output in YUV format, not RGBA format. Therefore, you need to be aware that the image won't be displayed normally, even if you directly specify the output data buffer to the frame buffer.
Also, data output in YUV format needs to be expressed as a 16x16 dot texture. Both input and output data regions are divided into 768-byte macro blocks, and each macro block processed completes in its region. As a result, the size of the output data becomes 512 byte for the 768 byte (Y4, U1, and V1 are each 128 bytes) after input to the macro block region. The 256-byte empty region (suitable for the unnecessary UV component region) is produced. (Refer to "3.1.3 Output Data")
When you get the texture from the data buffer, add the pointer to the buffer at each 768-byte boundary.
Please refer to Sample Program for the specific methods.