Read and write BMP file in C
And a simple test. I feel this one is a little messy. I’m not so sure about the helper functions I’ve made.
#include #include // for EXIT_SUCCESS and EXIT_FAILURE #include "bmp.h" BMPImage *read_image(const char *filename, char **error); void write_image(const char *filename, BMPImage *image, char **error); FILE *_open_file(const char *filename, const char *mode); void _handle_error(char **error, FILE *fp, BMPImage *image); void _clean_up(FILE *fp, BMPImage *image, char **error); int main(void) < char *error = NULL; BMPImage *image = read_image("6x6_24bit.bmp", &error); write_image("copy.bmp", image, &error); BMPImage *crop_image = crop_bmp(image, 2, 3, 4, 2, &error); write_image("crop.bmp", crop_image, &error); bool is_valid = check_bmp_header(&crop_image->header, fopen("crop.bmp", "rb")); _clean_up(NULL, image, &error); _clean_up(NULL, crop_image, &error); return EXIT_SUCCESS; > BMPImage *read_image(const char *filename, char **error) < FILE *input_ptr = _open_file(filename, "rb"); BMPImage *image = read_bmp(input_ptr, error); if (*error != NULL) < _handle_error(error, input_ptr, image); >fclose(input_ptr); return image; > void write_image(const char *filename, BMPImage *image, char **error) < FILE *output_ptr = _open_file(filename, "wb"); if (!write_bmp(output_ptr, image, error)) < _handle_error(error, output_ptr, image); >fclose(output_ptr); > /* * Open file. In case of error, print message and exit. */ FILE *_open_file(const char *filename, const char *mode) < FILE *fp = fopen(filename, mode); if (fp == NULL) < fprintf(stderr, "Could not open file %s", filename); exit(EXIT_FAILURE); >return fp; > /* * Print error message and clean up resources. */ void _handle_error(char **error, FILE *fp, BMPImage *image) < fprintf(stderr, "ERROR: %s\n", *error); _clean_up(fp, image, error); exit(EXIT_FAILURE); >/* * Close file and release memory. */ void _clean_up(FILE *fp, BMPImage *image, char **error) < if (fp != NULL) < fclose(fp); >free_bmp(image); free(*error); >
Считать bmp побайтно и пересохранить

необходимо открыть bmp файл, считать заголовок(читаю оба в одну структуру)
считать растровый массив( с этим походу и есть беда)
произвести манипуляции с массивом(это пока опустим)
и сохранить результат в новый bmp (пока что просто перезаписать этот файл с другим именем)
пробую так
насколько я понял, беда с чтением растрового массива. как его правильно читать?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
#include int openBMP(char* path); int saveBMP(char* path); #pragma pack(push, 2) struct BmpHeader { unsigned char b1, b2; // Символы BM (2 байта) unsigned long size; // Размер файла (4 байта) unsigned short notUse1; // (2 байта) unsigned short notUse2; // (2 байта) unsigned long massPos; // Местанахождение данных растрового массива (4 байта) unsigned long headerLength; // Длинна этого заголовка (4 байта) unsigned long width; // Ширина изображения (4 байта) unsigned long height; // Высота изображения (4 байта) unsigned short colorPlaneNumber; // Число цветовых плоскостей (2 байта) unsigned short bitPixel; // Бит/пиксель (2 байта) unsigned long compressMethod; // Метод сжатия (4 байта) unsigned long massLength; // Длинна массива с мусоро (4 байта) unsigned long massWidth; // Ширина массива с мусором (4 байта) unsigned long massHeight; // Высота массива с мусором (4 байта) unsigned long colorNumber; // Число цветов изображения (4 байта) unsigned long generalColorNumber; // Число основных цветов (4 байта) } bmpHeader; #pragma pack(pop) unsigned char** img; int main(void) { if (readBMP("D:\\1.bmp") == 0) { printf("bad path"); return 0; } if (saveBMP("D:\\_1.bmp") == 0) { printf("bad path"); return 0; } return 0; } int readBMP(char* path) FILE* file; int i, j; file = fopen(path,"rb"); if(file == NULL) return 0; fread(&bmpHeader, sizeof(bmpHeader), 1, file); if (bmpHeader.b1 != 'B' img = (unsigned char**)calloc(bmpHeader.width, sizeof(*img)); for(i = 0; i bmpHeader.width; ++i) img[i] = (unsigned char*)calloc(bmpHeader.height, sizeof(*img[i])); for (i = 0; i bmpHeader.width; i++) { for (j = 0; j bmpHeader.height; j++) { fread(&img[i][j], sizeof(img[i][j]), 1, file); } } fclose(file); return 1; } int saveBMP(char* path) { FILE* file; int i, j; file = fopen(path,"wb"); if(file == NULL) return 0; fwrite(&bmpHeader, sizeof(bmpHeader), 1, file); for (i = 0; i bmpHeader.width; i++) { for (j = 0; j bmpHeader.height; j++) { fwrite(&img[i][j], sizeof(img[i][j]), 1, file); } } fclose(file); return 1; }
Read and write bmp files in c/c++
The BMP format, often referred to as the Windows Bitmap Format is in my opinion, one of the simplest ways to store image information in a computer. Even though it was originally defined by Microsoft for its operating systems, nowadays its usage is widespread and can be recognized by practically every major imaging software. For the same reason, there are lots of libraries and APIs to work with images stored in this format.
The purpose of this post is to provide some guidance for those programmers who can’t or don’t want to use a third party library to work with images in the BMP format (e.g. targeting embedded platforms, developing sensitive proprietary software or for learning purposes). And even though our working example will be presented in the c language, it can be easily ported to other languages if necessary.
If you’re here just for the code, here’s a somewhat stripped-down implementation of the functions to open and save BMP files. Be aware that for legibility no safety validations are being performed (e.g. check for NULL pointers, validate file operations, etc.) as they are left to the reader. You can download the source code “bmp.c” and the sample image “img.bmp” from this link

#include #include #include #define DATA_OFFSET_OFFSET 0x000A #define WIDTH_OFFSET 0x0012 #define HEIGHT_OFFSET 0x0016 #define BITS_PER_PIXEL_OFFSET 0x001C #define HEADER_SIZE 14 #define INFO_HEADER_SIZE 40 #define NO_COMPRESION 0 #define MAX_NUMBER_OF_COLORS 0 #define ALL_COLORS_REQUIRED 0 typedef unsigned int int32; typedef short int16; typedef unsigned char byte; void ReadImage(const char *fileName,byte **pixels, int32 *width, int32 *height, int32 *bytesPerPixel) < FILE *imageFile = fopen(fileName, "rb"); int32 dataOffset; fseek(imageFile, DATA_OFFSET_OFFSET, SEEK_SET); fread(&dataOffset, 4, 1, imageFile); fseek(imageFile, WIDTH_OFFSET, SEEK_SET); fread(width, 4, 1, imageFile); fseek(imageFile, HEIGHT_OFFSET, SEEK_SET); fread(height, 4, 1, imageFile); int16 bitsPerPixel; fseek(imageFile, BITS_PER_PIXEL_OFFSET, SEEK_SET); fread(&bitsPerPixel, 2, 1, imageFile); *bytesPerPixel = ((int32)bitsPerPixel) / 8; int paddedRowSize = (int)(4 * ceil((float)(*width) / 4.0f))*(*bytesPerPixel); int unpaddedRowSize = (*width)*(*bytesPerPixel); int totalSize = unpaddedRowSize*(*height); *pixels = (byte*)malloc(totalSize); int i = 0; byte *currentRowPointer = *pixels+((*height-1)*unpaddedRowSize); for (i = 0; i < *height; i++) < fseek(imageFile, dataOffset+(i*paddedRowSize), SEEK_SET); fread(currentRowPointer, 1, unpaddedRowSize, imageFile); currentRowPointer -= unpaddedRowSize; >fclose(imageFile); > void WriteImage(const char *fileName, byte *pixels, int32 width, int32 height,int32 bytesPerPixel) < FILE *outputFile = fopen(fileName, "wb"); //*****HEADER************// const char *BM = "BM"; fwrite(&BM[0], 1, 1, outputFile); fwrite(&BM[1], 1, 1, outputFile); int paddedRowSize = (int)(4 * ceil((float)width/4.0f))*bytesPerPixel; int32 fileSize = paddedRowSize*height + HEADER_SIZE + INFO_HEADER_SIZE; fwrite(&fileSize, 4, 1, outputFile); int32 reserved = 0x0000; fwrite(&reserved, 4, 1, outputFile); int32 dataOffset = HEADER_SIZE+INFO_HEADER_SIZE; fwrite(&dataOffset, 4, 1, outputFile); //*******INFO*HEADER******// int32 infoHeaderSize = INFO_HEADER_SIZE; fwrite(&infoHeaderSize, 4, 1, outputFile); fwrite(&width, 4, 1, outputFile); fwrite(&height, 4, 1, outputFile); int16 planes = 1; //always 1 fwrite(&planes, 2, 1, outputFile); int16 bitsPerPixel = bytesPerPixel * 8; fwrite(&bitsPerPixel, 2, 1, outputFile); //write compression int32 compression = NO_COMPRESION; fwrite(&compression, 4, 1, outputFile); write image size (in bytes) int32 imageSize = width*height*bytesPerPixel; fwrite(&imageSize, 4, 1, outputFile); int32 resolutionX = 11811; //300 dpi int32 resolutionY = 11811; //300 dpi fwrite(&resolutionX, 4, 1, outputFile); fwrite(&resolutionY, 4, 1, outputFile); int32 colorsUsed = MAX_NUMBER_OF_COLORS; fwrite(&colorsUsed, 4, 1, outputFile); int32 importantColors = ALL_COLORS_REQUIRED; fwrite(&importantColors, 4, 1, outputFile); int i = 0; int unpaddedRowSize = width*bytesPerPixel; for ( i = 0; i < height; i++) < int pixelOffset = ((height - i) - 1)*unpaddedRowSize; fwrite(&pixels[pixelOffset], 1, paddedRowSize, outputFile); >fclose(outputFile); > int main()
The BMP file structure
The structure of a minimal BMP file looks like this:
| Name | Size | Offset (Hex) | Description/Notes |
| Bitmap File Header | |||
| BMP signature | 2 bytes | 0x00 | The characters ‘B’ and ‘M’ |
| File size | 4 bytes | 0x02 | Size in bytes of the file including headers and pixel data |
| Reserved | 4 bytes | 0x06 | Unused |
| Data offset | 4 bytes | 0x0A | Offset in the file where the pixel data is stored |
| Bitmap Information Header | |||
| Size of the header | 4 bytes | 0x0E | The header is fixed size: 40 bytes |
| Width | 4 bytes | 0x12 | Width of the image in pixels |
| Height | 4 bytes | 0x16 | Height of the image in pixels |
| Planes | 2 bytes | 0x1A | Number of color planes (must be 1) |
| Bits per pixel | 2 bytes | 0x1C | Number of bits per pixel |
| Compression | 4 bytes | 0x1E | Compression method |
| Image size | 4 bytes | 0x22 | Can be 0 if image is not compressed, otherwise is the size of the compressed image |
| Pixels per meter in X axis | 4 bytes | 0x26 | Horizontal resolution in pixels per meter |
| Pixels per meter in Y axis | 4 bytes | 0x2A | Vertical resolution in pixels per meter |
| Colors used | 4 bytes | 0x2E | Number used colors |
| Important colors | 4 bytes | 0x32 | Number of important color. Can be 0 If all colors are important |
| Color table | Only if Bits per pixel | ||
| Data | |||
| Pixel data | Variable | Variable | Stored bottom-up |
As the table shows, there are mainly 3 parts:
- Bitmap file header: provides information about the file itself.
- Bitmap information header: contains the properties of the pixel data.
- Data: the actual pixel data.
Most of the time you will be working with 24-bit RGB color or 8-bit grayscale images but it’s important to consider that in the case where the pixels are represented with less than 8 bits, a Color Table must be included in the bitmap information header. Other optional fields might be present in the file but for simplicity they are not covered in this post. For more information about this you can consult this Wikipedia article or this post by Nathan Liesch.
Reading a BMP file
To extract the pixel information you could decide to read every field from the header based on the table above. But in reality there are only 4 parts we’re interested in:
- The data offset (so you know where the pixel data is)
- Image height
- Image width
- Bits per pixel
This can be achieved with the regular fopen function by moving to the correct offset inside the file (using fseek):
#define DATA_OFFSET_OFFSET 0x000A #define WIDTH_OFFSET 0x0012 #define HEIGHT_OFFSET 0x0016 #define BITS_PER_PIXEL_OFFSET 0x001C FILE *imageFile = fopen(fileName, "rb"); int32 dataOffset; fseek(imageFile, DATA_OFFSET_OFFSET, SEEK_SET); fread(&dataOffset, 4, 1, imageFile); fseek(imageFile, WIDTH_OFFSET, SEEK_SET); fread(width, 4, 1, imageFile); fseek(imageFile, HEIGHT_OFFSET, SEEK_SET); fread(height, 4, 1, imageFile); int16 bitsPerPixel; fseek(imageFile, BITS_PER_PIXEL_OFFSET, SEEK_SET); fread(&bitsPerPixel, 2, 1, imageFile);Once we have this information we can allocate enough memory to contain the pixel data, that is height x width x bytes per pixel (bits per pixel divided by 8).
At this point you might be tempted to just read the rest of the file starting from the data offset. If you do, you might notice that your image is upside-down and it has some extra pixels. Our example image would look like this:
This is because the image is stored bottom-up and the rows contain padding bytes to make their size a multiple of 4. There are many ways of handling this situation. One option is to have a pointer to the last row of the (by now empty) pixel array, read a row from the file into that address (ignoring the padding bytes) and moving the pointer to the next row. The following code snippet illustrates this process:
//point to the last row of our pixel array (unpadded) byte *currentRowPointer = *pixels+((*height-1)*unpaddedRowSize); for (i = 0; i < *height; i++)
Now you have a pixel array to work with!
Writing BMP files
If you want other programs to be able to work with the a file you created, you need to adhere to the file’s structure specification. This is quite straightforward if you follow the table above. Look at the code sample for more details.
At this stage the key is to remember that rows must be padded to a multiple of 4 and stored bottom-up. To do this you can just reverse the process we used for reading the file:
for ( i = 0; i < height; i++) < //start writing from the beginning of last row in the pixel array int pixelOffset = ((height - i) - 1)*unpaddedRowSize; fwrite(&pixels[pixelOffset], 1, paddedRowSize, outputFile); >And with that piece of code we covered the most basic operations. The provided source code was tested on Windows, Ubuntu and RHEL, but you might need to make some changes for other targets.
Notes for Windows users:
Make sure to open the output file in binary mode. Remember that in Windows line endings are represented with two characters (\n and \r). For this reason, if your file is open in text mode, every time you write the value 10 (0x0A, the \n character) an extra 13 will be added (0x0D, the \r character). These kind of errors are very difficult to debug without a Hex editor and lot of patience.If you’re using Visual Studio you might experience problems with the fopen function. You can change it openf_s or add the _CRT_SECURE_NO_WARNINGS pre-processor definition as explained here.
You can use BITMAPFILEHEADER and BITMAPINFOHEADER to read/write the file header.
Notes for Linux users:
Since we’re using math.h remember to link your program with the -lm option (gcc and other compilers).Вывод информации о *.bmp на чистом C++
Dev-C++ это все компилировать не хочет (просто виснет), а VS 2008 выдает туеву хучу различных ошибок связанных с объявленными идентификаторами:
C:\Program Files\Microsoft Visual Studio 9.0\VC>cl new2.cpp Оптимизирующий 32-разрядный компилятор Microsoft (R) C/C++ версии 15.00.30729.01 для 80x86 (C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены. new2.cpp C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\xlocale(342) : warning C 4530: Использован обработчик исключений C++, но семантика уничтожения объектов н е включена. Задайте параметр /EHsc new2.cpp(41) : error C2059: синтаксическая ошибка: [ new2.cpp(52) : error C2062: тип "int" не требуется new2.cpp(72) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(76) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(80) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(84) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(88) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(90) : error C2065: OffBits: необъявленный идентификатор new2.cpp(90) : error C2668: pow: неоднозначный вызов перегруженной функции C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(575): мож ет быть 'long double pow(long double,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(527): или 'float pow(float,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(489): или 'double pow(double,int)' при попытке сопоставить список аргументов '(int, int)' new2.cpp(92) : error C2065: OffBits: необъявленный идентификатор new2.cpp(95) : error C2051: значение выражения для варианта выбора не является к онстантой new2.cpp(100) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(102) : error C2065: Width: необъявленный идентификатор new2.cpp(102) : error C2668: pow: неоднозначный вызов перегруженной функции C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(575): мож ет быть 'long double pow(long double,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(527): или 'float pow(float,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(489): или 'double pow(double,int)' при попытке сопоставить список аргументов '(int, int)' new2.cpp(105) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(107) : error C2065: Height: необъявленный идентификатор new2.cpp(107) : error C2668: pow: неоднозначный вызов перегруженной функции C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(575): мож ет быть 'long double pow(long double,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(527): или 'float pow(float,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(489): или 'double pow(double,int)' при попытке сопоставить список аргументов '(int, int)' new2.cpp(110) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(111) : error C2109: для индекса требуется массив или указатель new2.cpp(114) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(115) : error C2065: biBitCount: необъявленный идентификатор new2.cpp(118) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(119) : error C2065: biCompression: необъявленный идентификатор new2.cpp(122) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(123) : error C2065: biSizeImage: необъявленный идентификатор new2.cpp(124) : error C2065: Size: необъявленный идентификатор new2.cpp(124) : error C2065: biSizeImage: необъявленный идентификатор new2.cpp(124) : error C2668: pow: неоднозначный вызов перегруженной функции C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(575): мож ет быть 'long double pow(long double,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(527): или 'float pow(float,int)' C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\math.h(489): или 'double pow(double,int)' при попытке сопоставить список аргументов '(int, int)' new2.cpp(127) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(128) : error C2065: biXPelsPerMeter: необъявленный идентификатор new2.cpp(131) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(132) : error C2065: biYPelsPerMeter: необъявленный идентификатор new2.cpp(135) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(136) : error C2065: biClrUsed: необъявленный идентификатор new2.cpp(139) : error C2051: значение выражения для варианта выбора не является константой new2.cpp(140) : error C2065: biClrImpotant: необъявленный идентификатор new2.cpp(145) : error C2065: Width: необъявленный идентификатор new2.cpp(146) : error C2065: Height: необъявленный идентификатор new2.cpp(148) : error C2057: требуется константное выражение new2.cpp(148) : error C2466: невозможно выделить память для массива постоянного нулевого размера new2.cpp(148) : error C2057: требуется константное выражение new2.cpp(148) : error C2466: невозможно выделить память для массива постоянного нулевого размера new2.cpp(148) : error C2087: pixels: отсутствует индекс new2.cpp(148) : error C2133: pixels: неизвестный размер new2.cpp(151) : error C2065: Size: необъявленный идентификатор new2.cpp(152) : error C2065: Width: необъявленный идентификатор new2.cpp(153) : error C2065: Height: необъявленный идентификаторХотя идентификаторы объявлены. В чем моя ошибка?
P.S. Программа до конца не доделана (есть лишние переменные, но на саму работу программы они повлиять не должны)
