读取并复制TGA图像文件时出现损坏问题求助
TGA文件完整复制程序问题修复
需求是通过复制.tga文件的所有数据,生成与原文件完全一致的新文件,但现有C程序复制后部分图像轻微损坏,部分显示全随机像素,以下是问题分析和修复方案:
原程序代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <assert.h> typedef unsigned char byte; typedef struct { byte id_length; byte color_map_type; byte image_type; byte color_map[5]; byte x_origin[2]; byte y_origin[2]; byte width[2]; byte height[2]; byte depth; byte descriptor; } TGAHeader; typedef struct { byte red; byte green; byte blue; } RGBPixel; typedef struct { TGAHeader header; RGBPixel* pixels; int width; int height; } Image; bool image_load(Image* image, const char* path){ FILE *file = fopen(path, "rb"); memset(image, 0, sizeof(Image)); fread(&image->header, sizeof(image->header), 1, file); assert(image->header.depth == 24); assert(image->header.image_type == 2); memcpy(&image->width, image->header.width, sizeof(image->header.width)); memcpy(&image->height, image->header.height, sizeof(image->header.height)); image->pixels = (RGBPixel*) malloc(sizeof(RGBPixel) * image->width * image->height); fread(image->pixels, sizeof(RGBPixel) * image->width * image->height, 1, file); fclose(file); return true; } void image_free(Image* image) { free(image->pixels); } int main(int argc, char* argv[]){ Image image; if(!image_load(&image, argv[1])){ printf("Could not load image"); return 1; } FILE* fp; fp = fopen(argv[2], "wb"); fwrite(&image, 1, sizeof(image), fp); fclose(fp); image_free(&image); return 0; }
核心问题分析
- 错误的文件写入方式:原程序直接
fwrite(&image, 1, sizeof(image), fp),但Image结构体中的pixels是指针,写入的是指针的内存地址而非实际像素数据,这是导致图像全随机的根本原因。 - 字节序未处理:TGA文件的宽高是小端字节序存储,原程序用
memcpy直接转成int,在大端系统上会解析出错误的宽高,导致像素读取/写入范围错误。 - 忽略ID字段:TGA头的
id_length可能大于0,文件开头包含ID数据,原程序未读取这部分,导致后续像素数据偏移,引发图像轻微损坏。 - 缺失错误检查:
fopen、fread、malloc等关键操作未检查返回值,出错后无法定位问题。
修复后的程序代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> typedef unsigned char byte; typedef struct { byte id_length; byte color_map_type; byte image_type; byte color_map[5]; byte x_origin[2]; byte y_origin[2]; byte width[2]; byte height[2]; byte depth; byte descriptor; } TGAHeader; typedef struct { byte blue; byte green; byte red; } BGRPixel; // 匹配TGA原生BGR像素顺序 typedef struct { TGAHeader header; BGRPixel* pixels; int width; int height; byte* id_data; } Image; bool image_load(Image* image, const char* path){ FILE *file = fopen(path, "rb"); if (!file) { perror("Failed to open input file"); return false; } memset(image, 0, sizeof(Image)); // 读取TGA头 if (fread(&image->header, sizeof(TGAHeader), 1, file) != 1) { perror("Failed to read TGA header"); fclose(file); return false; } // 仅支持24位无压缩真彩色TGA if (image->header.depth != 24 || image->header.image_type != 2) { fprintf(stderr, "Unsupported TGA format\n"); fclose(file); return false; } // 读取ID字段(如果存在) if (image->header.id_length > 0) { image->id_data = malloc(image->header.id_length); if (!image->id_data) { perror("Failed to allocate ID data memory"); fclose(file); return false; } if (fread(image->id_data, image->header.id_length, 1, file) != 1) { perror("Failed to read ID data"); free(image->id_data); fclose(file); return false; } } // 小端字节序解析宽高 image->width = (image->header.width[1] << 8) | image->header.width[0]; image->height = (image->header.height[1] << 8) | image->header.height[0]; // 分配像素内存 size_t pixel_count = (size_t)image->width * image->height; image->pixels = malloc(sizeof(BGRPixel) * pixel_count); if (!image->pixels) { perror("Failed to allocate pixel memory"); free(image->id_data); fclose(file); return false; } // 读取像素数据 if (fread(image->pixels, sizeof(BGRPixel), pixel_count, file) != pixel_count) { perror("Failed to read pixel data"); free(image->pixels); free(image->id_data); fclose(file); return false; } fclose(file); return true; } void image_free(Image* image) { free(image->pixels); free(image->id_data); } int main(int argc, char* argv[]){ if (argc != 3) { fprintf(stderr, "Usage: %s <input.tga> <output.tga>\n", argv[0]); return 1; } Image image; if(!image_load(&image, argv[1])){ fprintf(stderr, "Could not load image\n"); return 1; } FILE* fp = fopen(argv[2], "wb"); if (!fp) { perror("Failed to open output file"); image_free(&image); return 1; } // 写入TGA头 if (fwrite(&image.header, sizeof(TGAHeader), 1, fp) != 1) { perror("Failed to write TGA header"); fclose(fp); image_free(&image); return 1; } // 写入ID字段 if (image.header.id_length > 0) { if (fwrite(image.id_data, image.header.id_length, 1, fp) != 1) { perror("Failed to write ID data"); fclose(fp); image_free(&image); return 1; } } // 写入像素数据 size_t pixel_count = (size_t)image.width * image.height; if (fwrite(image.pixels, sizeof(BGRPixel), pixel_count, fp) != pixel_count) { perror("Failed to write pixel data"); fclose(fp); image_free(&image); return 1; } fclose(fp); image_free(&image); return 0; }
修复要点
- 拆分写入操作:分别写入TGA头、ID字段(如果存在)、实际像素数据,避免写入指针地址。
- 正确解析小端字节序的宽高值,保证跨系统兼容性。
- 处理TGA文件的ID字段,完整保留原文件所有数据。
- 匹配TGA原生BGR像素顺序,避免颜色反转问题。
- 增加所有IO和内存分配的错误检查,便于调试定位问题。
内容的提问来源于stack exchange,提问作者PHOENIX33201
相关产品推荐
相关产品推荐

