# 前言

实现软光栅渲染第一步,先要个画布,目前是直接画在 TGA 图像上,之后会考虑使用图形界面呈现。
实际关于 TGA 文件解析的文章有很多,写的也很详细,写这个主要是为了记录我实现的 TGA 图像读取和生成的大致过程,避免自己之后看不懂自己写的代码。
完整的代码地址在此

# TGA 文件格式

# TGA 文件头

TGA 文件头一共分为五个部分,如下图

位域长度类型描述
11byteId length取值 0~255. 0 表示无 id 段
21byteColor map type0: 不使用颜色表;
1: 使用颜色表
31byteImage type0~127 为 Truevision 公司用于一般应用,128~255 供开发者使用.
0: 没有图像数据;
1: 未压缩的颜色表图像;
2: 未压缩的 TRUE-COLOR 图像;
3: 未压缩的黑白图像;
9: RLE 压缩的颜色表图像;
10: RLE 压缩的 TRUE-COLOR 图像;
11: 压缩的黑白图像.
45byteColor map specification若 Header.Color map type = 0, 则没有颜色表存在,这 5byte 应全为 0.
2byteFirst Entry Index颜色表起点,即颜色表第一项的索引
2byteColor map Length颜色表长度,即颜色表共有多少项
1byteColor map Entry Size颜色表项大小,即每个项占用 bit 数。典型值 15, 16, 24, 32. 即使是 15, 每次也要用 16bit 解析,然后忽略第 16bit
510byteImage specification描述图像维度和格式
2byteX origin of Image图像起点 (左下角) x 坐标。基于屏幕原点在左下角
2byteY origin of Image图像起点 (左下角) y 坐标。基于屏幕原点在左下角
2byteImage Width图像宽度 (单位: pixel)
2byteImage Height图像高度 (单位: pixel)
1bytePixel Depth像素深度,即每个像素的 bit 数,包含属性或 α 通道。常用值 8, 16, 24, 32
1byteImage Descriptor0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)
1: undefined data in the Alpha field, can be ignored
2: undefined data in the Alpha field, but should be retained
3: useful Alpha channel data is present
4: pre-multiplied Alpha (see description below)
5 -127: RESERVED
128-255: Un-assigned

# RLE 压缩算法

行程编码压缩 (RLE) 算法利用了这样一个事实,即许多类型的图像都有很大的部分里的像素值都是相同的。因此可以把相同的连续的像素进行压缩存储。

RLE 图像包括两种类型的数据元素:行程数据包 (Run-length Packets) 和原始数据包 (Raw Packets), 这两个 Packets 的数据结构是一样的,如下表格。

Field NamePacket TypePixel CountPixel Data
Field Size1bit7bitsVariable

注: pixel Count 为 7bits + 1, 假设 7bits 表示 12,则实际的 piexl Count 为 13

# 行程数据包 (Run-length Packets)

当 Packet Type 为 1 时,该数据为行程数据包,是已压缩的图像数据。假设有个行程数据包如下

Packet TypePixel CountPixel Data
100000110x12

该行程数据包是单通道的像素数据,且数量为 4 个,则最终解析出的图像数据为 0x12 0x12 0x12 0x12

# 原始数据包 (Raw Packets)

当 Packet Type 为 0 时,该数据为原始数据包,是未压缩图像数据。假设有个原始数据包如下

Packet TypePixel CountPixel Data
000000110x12 0x11 0x10 0x13

该原始数据包是单通道的像素数据,且数量为 4 个,则最终解析出的图像数据为 0x12 0x11 0x10 0x13

# TGA 图像生成

对于渲染生成的 TGA 图像,支持存储为不进行 RLE 压缩的真彩色图像或黑白图像 (包括 α 通道),像素深度支持 8、16、24、32 位,不使用颜色表查询。

typedef struct {
    unsigned char* data;
    int width, height, channels;
    float* depthBuffer; // z-buffer
}image_t;
static void tga_save(FILE* file, image_t *image) {
    // write tga header to file
    unsigned char tgaHeader[18];
    memset(tgaHeader, 0, 18);
    tgaHeader[2] = image->channels != 1 ? 2 : 3;                        // image type 
    tgaHeader[12] = (unsigned char)(image->width & 0xFF);               // image width lsb
    tgaHeader[13] = (unsigned char)((image->width >> 8) & 0xFF);        // image width msb
    tgaHeader[14] = (unsigned char)(image->height & 0xFF);              // image height lsb
    tgaHeader[15] = (unsigned char)((image->height >> 8) & 0xFF);       // image height msb
    tgaHeader[16] = (unsigned char)((image->channels * 8) & 0xFF);      // pixel depth
    tgaHeader[17] = (image->channels == 2 || image->channels == 4) * 8; // image descriptor
    fwrite(tgaHeader, 1, 18, file);
    // write image data to file
    fwrite(image->data, 1, image->width * image->height * image->channels, file);
}

# TGA 图像加载

目前个人对 RLE 图像加载的使用场景很简单,图像没有使用颜色表,因此只是读取 TGA 文件头的图像大小和通道以及是否使用了 RLE 压缩算法这些信息,若未使用 RLE 算法,则把图像信息一次性加载出来,若使用了 RLE 算法,则进行解码获取图像数据。

static image_t *tga_load(FILE *file) {
    // read tga file header
    int width, height, channels;
    bool isRLE;
    read_tga_header(file, &width, &height, &channels, &isRLE);
    image_t *image = image_new(width, height, channels);
    if (isRLE) {
       load_rle_image(file, image);
    } else {
       fread(image->data, 1, image->width * image->height * image->channels, file);
    }
    return image;
}
static void load_rle_image(FILE* file, image_t* image) {
    int totalPixels = image->width * image->height * image->channels;
    int currentPixels = 0;
    while (currentPixels < totalPixels) {
        unsigned char firstFiled = fgetc(file);
        bool rlePacket = firstFiled & 0x80;
        int pixelCount = (firstFiled & 0x7F) + 1;
        if (rlePacket) { /* Run-Length Packet */
            unsigned char pixels[4];
            fread(pixels, 1, image->channels, file);
            for (int i = 0; i < pixelCount; i++) {
                for (int j = 0; j < image->channels; j++) {
                    image->data[currentPixels++] = pixels[j];
                }
            }
        } else { /* Raw Packet */
          for (int i = 0; i < pixelCount; i++) {
            for (int j = 0; j < image->channels; j++) {
              image->data[currentPixels++] = fgetc(file); 
            }
          }
        }
    }
}

# renference

【数据压缩】TGA 文件格式分析
为 GLUT 应用编写 TGA 图像加载程序
Truevision TGAª FILE FORMAT SPECIFICATION Version 2.0
自制光栅渲染器(1):绘制一个点
TGA 文件格式

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Jelly27th 微信支付

微信支付

Jelly27th 支付宝

支付宝