Реализация ввода и вывода картинки в .bmp

Возникла проблема, после отработки кода, создается output.bmp и пишет - "данный формат не поддерживается"

#define _CRT_SECURE_NO_WARNINGS
typedef unsigned __int16 GORA;
typedef struct {
    GORA   bfType;         // 0x4d42 | 0x4349 | 0x5450
    int    bfSize;         // размер файла
    int    bfReserved;     // 0
    int    bfOffBits;      // смещение до поля данных,
                           // обычно 54 = 16 + biSize
    int    biSize;         // размер структуры в байтах:
                           // 40(BITMAPINFOHEADER) или 108(BITMAPV4HEADER)
                           // или 124(BITMAPV5HEADER)
    int    biWidth;        // ширина в точках
    int    biHeight;       // высота в точках
    GORA   biPlanes;       // всегда должно быть 1
    GORA   biBitCount;     // 0 | 1 | 4 | 8 | 16 | 24 | 32
    int    biCompression;  // BI_RGB | BI_RLE8 | BI_RLE4 |
                           // BI_BITFIELDS | BI_JPEG | BI_PNG
                           // реально используется лишь BI_RGB
    int    biSizeImage;    // Количество байт в поле данных
                           // Обычно устанавливается в 0
    int    biXPelsPerMeter;// горизонтальное разрешение, точек на дюйм
    int    biYPelsPerMeter;// вертикальное разрешение, точек на дюйм
    int    biClrUsed;      // Количество используемых цветов
                           // (если есть таблица цветов)
    int    biClrImportant; // Количество существенных цветов.
                           // Можно считать, просто 0
} BMPheader;
#include <stdio.h>
#include <iostream>
using namespace std;
int* loadBMP(const char* fname, int& mx, int& my)
{
    mx = my = -1;
    FILE* f = fopen(fname, "rb");
    if (!f) return NULL;
    BMPheader bh;    // File header sizeof(BMPheader) = 56
    size_t res;

    // читаем заголовок
    res = fread(&bh, 1, sizeof(BMPheader), f);
    if (res != sizeof(BMPheader)) { fclose(f); return NULL; }

    // проверяем сигнатуру
    if (bh.bfType != 0x4d42 && bh.bfType != 0x4349 && bh.bfType != 0x5450) { fclose(f); return NULL; }

    // проверка размера файла
    fseek(f, 0, SEEK_END);
    int filesize = ftell(f);
    // восстановим указатель в файле:
    fseek(f, sizeof(BMPheader), SEEK_SET);
    // проверим условия
    if (bh.bfSize != filesize ||
        bh.bfReserved != 0 ||
        bh.biPlanes != 1 ||
        (bh.biSize != 40 && bh.biSize != 108 && bh.biSize != 124) ||
        bh.bfOffBits != 14 + bh.biSize ||

        bh.biWidth < 1 || bh.biWidth >10000 ||
        bh.biHeight < 1 || bh.biHeight>10000 ||
        bh.biBitCount != 24 ||             // пока рассматриваем только полноцветные изображения
        bh.biCompression != 0                // пока рассматриваем только несжатие изображения
        )
    {
        fclose(f);
        return NULL;
    }
    // Заголовок прочитан и проверен, тип - верный (BGR-24), размеры (mx,my) найдены
    mx = bh.biWidth;
    my = bh.biHeight;
    int mx3 = (3 * mx + 3) & (-4);    // Compute row width in file, including padding to 4-byte boundary
    unsigned char* tmp_buf = new unsigned  char[mx3 * my];    // читаем данные
    res = fread(tmp_buf, 1, mx3 * my, f);
    if ((int)res != mx3 * my) { delete[]tmp_buf; fclose(f); return NULL; }
    // данные прочитаны
    fclose(f);

    // выделим память для результата
   int *v = new int[mx * my];

    // Перенос данных (не забудем про BGR->RGB)
    unsigned char* ptr = (unsigned char*)v;
    for (int y = my - 1; y >= 0; y--) {
        unsigned char* pRow = tmp_buf + mx3 * y;
        for (int x = 0; x < mx; x++) {
            *ptr++ = *(pRow + 2);
            *ptr++ = *(pRow + 1);
            *ptr++ = *pRow;
            pRow += 3;
            ptr++;
        }
    }
    delete[]tmp_buf;
    return v;    // OK
}
int saveBMP(const char* fname, int* v, int mx, int my)  // В каждом элементе упаковано все три RGB-байта
{
    BMPheader bh;   // Заголовок файла, sizeof(BMPheader) = 56
    memset(&bh, 0, sizeof(bh));
    bh.bfType = 0x4d42; // 'BM'
    // Найдем длину строки в файле, включая округление вверх до кратного 4:
    int mx3 = (3 * mx + 3) & (-4);
    int filesize = 54 + my * mx3;
    bh.bfSize = filesize;
    bh.bfReserved = 0;
    bh.biPlanes = 1;
    bh.biSize = 40;
    bh.bfOffBits = 14 + bh.biSize;
    bh.biWidth = mx;
    bh.biHeight = my;
    bh.biBitCount = 24;
    bh.biCompression = 0;

    FILE* f = fopen(fname, "wb");
    if (!f) return -1;
    size_t res;

    // пишем заголовок
    res = fwrite(&bh, 1, sizeof(BMPheader), f);
    if (res != sizeof(BMPheader)) { fclose(f); return -1; }

    // приготовим временный буфер
    unsigned char* tmp_buf = new unsigned char[mx3 * my];
    // Перенос данных (не забудем про RGB->BGR)
    unsigned char* ptr = (unsigned char*)v;
    for (int y = my - 1; y >= 0; y--) {
        unsigned char* pRow = tmp_buf + mx3 * y;
        for (int x = 0; x < mx; x++) {
            *(pRow + 2) = *ptr++;
            *(pRow + 1) = *ptr++;
            *pRow = *ptr++;
            pRow += 3;
            ptr++;
        }
    }
    // сбросим в файл
    fwrite(tmp_buf, 1, mx3 * my, f);
    fclose(f);
    delete[]tmp_buf;
    return 0;   // OK
}
int main()
{
    int mx, my;
    int* v = loadBMP("input.bmp", mx, my);
    saveBMP("output.bmp", v, mx, my);
    return 0;
}

Ответы (1 шт):

Автор решения: Павел Ериков

Проблема у вас в самой структуру BMPheader. Дело в том, что она не упакована, и появляются лишние биты.

Например сначала у вас идет двухбайтовое число, (X - занятые байты)

X X 0 0
0 0 0 0

И далее в структуре у вас число четырехбайтовое, и оно без упаковки устанавливается на позицию, которая нацело делится на 4. В нашем случае это 0. То есть вот так:

X X 0 0
X X X X

Как вы видите у нас хоть и заняты 6 байт, но структуру будет весить 8.

И есть два варианта решения этой проблемы:

  1. Переставить переменные в структуре таким образом, чтобы не образовались лишние байты. Это сделать не сложно, но тогда вы не сможете в одну строчку считать всю структуру. Я думаю вам это не подходит.

  2. Упаковать структуру

    Для этого пишем следующее:

    #pragma pack(push, 1)
    typedef struct {
    ...
    } BMPheader;
    #pragma pack(pop)
    

    Если вкратце то #pragma pack в нашем случае позволяет нам задать выравнивание упаковки членов структуры.

    И теперь четырехбайтовое число будет устанавливаться вот так:

    X X X X
    X X
    

Поподробней можете почитать отличную статью на habr

→ Ссылка