C чтение файлов BMP

Я пытаюсь прочитать в BMP-файл моей программы, и у меня возникли некоторые проблемы. После прочтения в файле, если я скажу ему напечатать pBmp->header.fileSize, будет написано 16, но если я посмотрю на него в шестнадцатеричном редакторе, часть размера файла будет иметь F6 7A 10 00, если я изменю значение на правильный размер файла в гексе будет написано F6 7A F6 7A 10 00, но это приводит к resv1, который всегда должен быть равен нулю. Я знаю, что это только чтение в 1 пикселе данных. Другая проблема, с которой я сталкиваюсь, - это когда я пытаюсь использовать цикл while для чтения в пикселях до конца файла, я получаю ошибку сегментации. Я буквально часами гуглял, пытаясь понять это, но мне не повезло.

// The BMPHEADER structure.
typedef struct {
    byte        sigB;
    byte        sigM;
    int32_t     fileSize;
    int16_t     resv1;
    int16_t     resv2;
    int32_t     pixelOffset;
} tBmpHeader;

// The BMPINFOHEADER structure.
typedef struct {
    int32_t     size;
    int32_t     width;
    int32_t     height;
    int16_t     colorPlanes;
    int16_t     bitsPerPixel;
    byte        zeros[24];
} tBmpInfoHeader;

typedef uint8_t byte;

typedef struct {
    byte blue;
    byte green;
    byte red;
} tPixel;

// A BMP image consists of the BMPHEADER and BMPINFOHEADER structures, and the 2D pixel array.
typedef struct {
    tBmpHeader      header;
    tBmpInfoHeader  infoHeader;
    tPixel          **pixel;
} tBmp;

tPixel **BmpPixelAlloc(int pWidth, int pHeight)
{
    tPixel **pixels = (tPixel **)malloc (pHeight * sizeof(tPixel *));
    for (int row = 0; row < pHeight; ++row)
    {
        pixels[row] = (tPixel *)malloc(pWidth * sizeof(tPixel));
    }

    printf("pixelAlloc\n"); 

    return pixels;
}

pBmp->pixel = BmpPixelAlloc(pBmp->infoHeader.width, pBmp->infoHeader.height);
if(FileRead(file, &pBmp->pixel, sizeof(tPixel), 1)!=0)
{ 
    errorCode = ErrorFileRead;
}

2 ответа

Решение

Проверьте заполнение структуры для заголовка. Вы можете обнаружить, что компилятор выровнял fileSize значение в struct до 4-байтовой границы внутри структуры. Вы можете увидеть это, если добавите отладку printfс адресами элементов структуры.

Я запустил пример, используя ваш struct Вот:

typedef struct {
    byte        sigB;
    byte        sigM;
    int32_t     fileSize;
    int16_t     resv1;
    int16_t     resv2;
    int32_t     pixelOffset;
} tBmpHeader;

tBmpHeader hdr;

int main(int argc, char *argv[])
{
    printf("%d\n", sizeof(tBmpHeader) );
    printf("%p hdr\n", &hdr);
    printf("%p sigB\n", &hdr.sigB);
    printf("%p sigM\n", &hdr.sigM);
    printf("%p fileSize\n", &hdr.fileSize);
    printf("%p resv1\n", &hdr.resv1);
    printf("%p resv2\n", &hdr.resv2);
    printf("%p pixelOffset\n", &hdr.pixelOffset);
}

С выходом:

16
0x8049788 hdr
0x8049788 sigB
0x8049789 sigM
0x804978c fileSize
0x8049790 resv1
0x8049792 resv2
0x8049794 pixelOffset

Существует 4-байтовое смещение между началом hdr и fileSize элемент, поэтому перед заполнением есть два байта fileSize,

Что нужно сделать, это прочитать заголовок элемента даты за один раз. Вы можете поддерживать некоторую "структуру" в коде, инкапсулируя чтение в одну функцию (например, "readBmpHeader(...)"). Вы можете сохранить struct но читайте каждое поле отдельно. Итак, вы бы сделали (я оставляю проверки возвращаемого значения для ясности примера):

FileRead(file, &pBmp->header.sigB, sizeof(byte), 1)
FileRead(file, &pBmp->header.sigB, sizeof(byte), 1)
FileRead(file, &pBmp->header.fileSize, sizeof(int32_t), 1)
FileRead(file, &pBmp->header.resv1, sizeof(int16_t), 1)
FileRead(file, &pBmp->header.resv2, sizeof(int16_t), 1)
FileRead(file, &pBmp->header.pixelOffset, sizeof(int32_t), 1)

Вы никогда не должны использовать прямой ввод / вывод со структурами: только потому, что вы объявили struct в том же порядке, что и заголовки BMP, нет никакой гарантии, что ваш компилятор имеет сквозные поля, между которыми нет ничего.

Компиляторы часто придерживаются ограничений на выравнивание платформы, что может привести к добавлению байтов заполнения между полями, чтобы убедиться, что, например, начальный адрес большого поля выровнен.

Вам нужно либо использовать магию, специфичную для компилятора, чтобы заставить структуру быть "упакованной", либо побайтовый ввод-вывод в структуру.

Другие вопросы по тегам