You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读取并复制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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:16:08