仓库源文站点原文


author: caol64 title: PS2 Texture Image Encoding Algorithm "A1B5G5R5" slug: ps2-texture-encoding-algorithm-a1b5g5r5 description: Delve into the unique texture mapping coding algorithm, A1B5G5R5, utilized by PS2. Our detailed guide takes you through the principle behind this lossy yet efficient form of image encoding that significantly reduces image size. date: 2023-10-20 14:48:31 draft: false ShowToc: true TocOpen: true tags:


In the previous article, we discussed the RLE image compression algorithm used in PS2. This time, let's delve into its texture mapping encoding algorithm—A1B5G5R5.

Introduction

For textures, common image encoding formats like jpg or png are not suitable. This is because images are read and rendered by the GPU. You wouldn't want to send a jpg image over and have the GPU decode the entire image just to read one pixel, right? Therefore, the most ideal image format is an uncompressed bitmap format, allowing direct access to RGB data based on pixel coordinates. The A1B5G5R5 format we're introducing today is one such encoding format.

Analysis

The above two texture images are extracted from PS2 archives, stored in bitmap format with a pixel count of 128x128.

In a standard 32-bit bitmap, each pixel occupies 4 bytes of data, storing data for the RGBA channels. Therefore, the size of the two texture images above is 128x128x4 bytes. The data structure for each pixel in 32-bit RGBA format is as follows:

For the A1B5G5R5 encoding, each pixel occupies 2 bytes of data, with the alpha channel using 1 bit, and the other three colors each using 5 bits. The data structure for each pixel is as follows:

Decoding A1B5G5R5 into 32-bit RGBA can be achieved using the method depicted in the following image:

The pseudo-code is as follows:

while tex_offset < len(self.texture):
    b = tex_struct.unpack_from(self.texture, tex_offset)[0]
    out[rgb_tex_offset] = (b & 0x1F) << 3 # R
    out[rgb_tex_offset + 1] = ((b >> 5) & 0x1F) << 3 # G
    out[rgb_tex_offset + 2] = ((b >> 10) & 0x1F) << 3 # B
    rgb_tex_offset += 3
    tex_offset += tex_struct.size

It's evident that encoding a 32-bit RGBA image into a 16-bit A1B5G5R5 format results in the loss of the last 3 bits of data for each color, making it a lossy encoding format. However, the benefit is a compression ratio of 2:1, effectively halving the size of the image. When combined with the RLE encoding discussed in the previous article, further reduction in image size can be achieved.

Conclusion

Finally, here are the rendered effects of the two texture images mentioned above. Do any of you remember these two games?