# 前言实现软光栅渲染第一步,先要个画布,目前是直接画在 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 解析,然后忽略第 16bit510byteImage specification描述图像维度和格式2byteX origin of Image图像起点 (左下角) x 坐标。基于屏幕原点在左下角2byteY origin of Image图像起点 (左下角) y 坐标。基于屏幕原点在左下角2byteImage Width图像宽度 (单位: pixel)2byteImage Height图像高度 (单位: pixel)1bytePixel Depth像素深度,即每个像素的 bit 数,包含属性或 α 通道。常用值 8, 16, 24, 321byteImage 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 ignored2: undefined data in the Alpha field, but should be retained3: useful Alpha channel data is present4: pre-multiplied Alpha (see description below)5 -127: RESERVED128-255: Un-assigned# RLE 压缩算法行程编码压缩 (RLE) 算法利用了这样一个事实,即许多类型的图像都有很大的部分里的像素值都是相同的。因此可以把相同的连续的像素进行压缩存储。
RLE 图像包括两种类型的数据元素:行程数据包 (Run-length Packets) 和原始数据包 (Raw Packets), 这两个 Packets 的数据结构是一样的,如下表格。
Field NamePacket TypePixel CountPixel DataField Size1bit7bitsVariable注: pixel Count 为 7bits + 1, 假设 7bits 表示 12,则实际的 piexl Count 为 13
# 行程数据包 (Run-length Packets)当 Packet Type 为 1 时,该数据为行程数据包,是已压缩的图像数据。假设有个行程数据包如下
Packet TypePixel CountPixel Data100000110x12该行程数据包是单通道的像素数据,且数量为 4 个,则最终解析出的图像数据为 0x12 0x12 0x12 0x12
# 原始数据包 (Raw Packets)当 Packet Type 为 0 时,该数据为原始数据包,是未压缩图像数据。假设有个原始数据包如下
Packet TypePixel CountPixel Data000000110x12 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 文件格式
CG C