Реализация ввода и вывода картинки в .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.
И есть два варианта решения этой проблемы:
Переставить переменные в структуре таким образом, чтобы не образовались лишние байты. Это сделать не сложно, но тогда вы не сможете в одну строчку считать всю структуру. Я думаю вам это не подходит.
Упаковать структуру
Для этого пишем следующее:
#pragma pack(push, 1) typedef struct { ... } BMPheader; #pragma pack(pop)Если вкратце то
#pragma packв нашем случае позволяет нам задать выравнивание упаковки членов структуры.И теперь четырехбайтовое число будет устанавливаться вот так:
X X X X X X
Поподробней можете почитать отличную статью на habr