Как вывести bmp файл с
| biBitCount | Палитровый или беспалитровый формат | Максимально возможное количество цветов | Примечания |
| 1 | Палитровый | 2 | Двуцветная, заметьте, не обязательно черно-белая, палитровая картинка. Если бит растра (что это такое чуть ниже) сброшен (равен 0), то это значит, что на этом месте должен быть первый цвет из палитры, а если установлен (равен 1), то второй. |
| 4 | Палитровый | 16 | Каждый байт описывает 2 пикселя. Вот пример из MSDN.Если первый байт в картинке 0x1F, то он соответствует двум пикселям, цвет первого — второй цвет из палитры (потому что отсчет идет от нуля), а второй пиксель — 16-й цвет палитры. |
| 8 | Палитровый | 256 | Один из самых распространенных вариантов. Но в то же время и самых простых. Палитра занимает один килобайт (но на это лучше не рассчитывать). Один байт — это один цвет. Причем его значение — это номер цвета в палитре. |
| 16 | Беспалитровый | 2^16 или 2^15 | Это самый запутанный вариант. Начнем с того, что он беспалитровый, то есть каждые два байта (одно слово WORD) в растре однозначно определяют один пиксель. Но вот что получается: битов-то 16, а компонентов цветов — 3 (Красный, Зеленый, Синий). А 16 никак на 3 делиться не хочет. Поэтому здесь есть два варианта. Первый — использовать не 16, а 15 битов, тогда на каждую компоненту цвета выходит по 5 бит. Таким образом мы можем использовать максимум 2^15 = 32768 цветов и получается тройка R-G-B = 5-5-5. Но тогда за зря теряется целый бит из 16. Но так уж случилось, что наши глаза среди всех цветов лучше воспринимают зеленый цвет, поэтому и решили этот один бит отдавать на зеленую компоненту, то есть тогда получается тройка R-G-B = 5-6-5, и теперь мы может использовать 2^16 = 65536 цветов. Но что самое неприятное, что используют оба варианта. В MSDN предлагают для того, чтобы различать сколько же цветов используется, заполнять этим значением поле biClrUsed из структуры BITMAPINFOHEADER. Чтобы выделить каждую компоненту надо использовать следующие маски. Для формата 5-5-5: 0x001F для синей компоненты, 0x03E0 для зеленой и 0x7C00 для красной. Для формата 5-6-5: 0x001F — синяя, 0x07E0 — зеленая и 0xF800 красная компоненты соответственно. |
| 24 | Беспалитровый | 2^24 | А это самый простой формат. Здесь 3 байта определяют 3 компоненты цвета. То есть по компоненте на байт. Просто читаем по структуре RGBTRIPLE и используем его поля rgbtBlue, rgbtGreen, rgbtRed. Они идут именно в таком порядке. |
| 32 | Беспалитровый | 2^32 | Здесь 4 байта определяют 3 компоненты. Но, правда, один байт не используется. Его можно отдать, например, для альфа-канала (прозрачности). Читать растр в данном случае удобно структурами RGBQUAD, которая описана так: |
| biBitCount | Формула на С |
| 8 | (3 * Width) % 4 |
| 16 | (2 * Width) % 4 |
| 24 | Width % 4 |
hFile = CreateFile ( fname, GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, 0 , NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
return ;
BOOL Convert256To24 ( char * fin, char * fout )
<
BITMAPFILEHEADER bfh ;
BITMAPINFOHEADER bih ;
int Width, Height ;
RGBQUAD Palette [ 256 ] ;
BYTE * inBuf ;
RGBTRIPLE * outBuf ;
HANDLE hIn, hOut ;
DWORD RW ;
DWORD OffBits ;
int i, j ;
hIn = CreateFile ( fin, GENERIC_READ, FILE_SHARE_READ, NULL , OPEN_EXISTING, 0 , NULL ) ;
if ( hIn == INVALID_HANDLE_VALUE )
return FALSE ;
hOut = CreateFile ( fout, GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, 0 , NULL ) ;
if ( hOut == INVALID_HANDLE_VALUE )
<
CloseHandle ( hIn ) ;
return FALSE ;
>
// Прочтем данные
ReadFile ( hIn, & bfh, sizeof ( bfh ) , & RW, NULL ) ;
ReadFile ( hIn, & bih, sizeof ( bih ) , & RW, NULL ) ;
ReadFile ( hIn, Palette, 256 * sizeof ( RGBQUAD ) , & RW, NULL ) ;
// Начнем преобразовывать
for ( i = 0 ; i < Height ; i ++ )
<
ReadFile ( hIn, inBuf, Width, & RW, NULL ) ;
for ( j = 0 ; j < Width ; j ++ )
<
outBuf [ j ] . rgbtRed = Palette [ inBuf [ j ] ] . rgbRed ;
outBuf [ j ] . rgbtGreen = Palette [ inBuf [ j ] ] . rgbGreen ;
outBuf [ j ] . rgbtBlue = Palette [ inBuf [ j ] ] . rgbBlue ;
>
WriteFile ( hOut, outBuf, sizeof ( RGBTRIPLE ) * Width, & RW, NULL ) ;
delete inBuf ;
delete outBuf ;
CloseHandle ( hIn ) ;
CloseHandle ( hOut ) ;
return TRUE ;
>
C++ reading and writing BMP images
In this article, I will show you how to implement a BMP image loader from scratch in C++. BMP is one of the oldest image formats on the Windows platform and it is supported on most other operating systems. BMP can store two-dimensional raster images with optional compression and transparency. In this article, we will implement a simplified version of the BMP format specification that will support only 24 and 32 bits depth images in the BGR and BGRA color spaces. To make our life simpler we can also ignore the, optional, compression component.
Even if you don’t plan to use BMP images, it is still a useful programming exercise to write a BMP reader/writer in C++.
From a programming point of view, a BMP file is a binary file in the little-endian format. For our purposes, we can divide a BMP image in four regions:
- file header — all BMP images starts with a five elements file header. This has information about the file type, file size and location of the pixel data.
- bitmap header — also named the info header. This has information about the width/height of the image, bits depth and so on.
- color header — contains informations about the color space and bit masks
- pixel data.
It is probably easier to show you directly the code for the file header:
The last field of the above structure represents the position, in bytes, from the start of the file to where the pixel data is stored.
If you want to read the header using the above structure, you’ll need to keep in mind that a compiler is free to add padding to a struct to align the data for a particular machine. With most modern C++ compilers (e.g. GCC, Clang, MSVC, Intel) you can use the next pragma pack syntax to ask for a specific alignment:
Without pragma pack the above struct takes 16 bytes on my machine, with the pragma pack instructions, same struct takes 14 bytes. You can obviously chose to read every field of the struct separately and avoid the padding problem, but it is more cumbersome and error prone.
The second region of a BMP file can be described by the next structure:
From the above we need to consider only the width, height, bit_count and compression. The compression is set to 0 for images with 24 bits per pixel and 3 for images with 32 bits per pixel.
The third region of can be described by:
The color masks are initialized to BGRA format and are only used for images with transparency (32 bits depth in our case).
A peculiarity of the BMP image format is that, if the height is negative, you have the origin of the image in the top left corner. If the height is a positive number, the origin of the image is at the bottom left corner. For simplicity, we will consider only the case when the image height is a positive number and the origin is always in the bottom left corner.
The BMP image format expects every row of data to be aligned to a four bytes boundary or padded with zero if this is not the case. For a 32 bits per pixel image the alignment condition is always satisfied. In the case of a 24 bits per pixel images, the alignment is satisfied only if the image width is divisible by 4, otherwise we’ll need to pad the rows with zeros.
Using the above two structs we can define a new BMP struct that can read/write a BMP image from disk, create a BMP object in memory, modify the pixel data and so on …
A possible implementation could look like this:
Suppose that we want to be able to read, write and directly modify the pixel data of a BMP image, something like in the next code:
Please note that bmp3 from the above code has a width of 209 pixel, which means it will need padding with zeros to align the rows to a 4 bytes boundary.
For testing purposes we’ll use a few images Shapes.bmp, Shapes-24.bmp, t1-24.bmp, t2-24.bmp please use the above links if you want to download the images in BMP format. The first image looks like this:

We’ll start by writing the code that loads the image from disk. Please note, that this is not intended to read all BMP image variations. It will work only with 32 or 24 bits per pixel, uncompressed, images in the format BGRA or BGR. I’ve tested the code with BMP images generated with GIMP, Paint.NET and Microsoft Paint.
In order to read the image, we need to open the image as a binary file, read the headers, use the image size information to resize the data vector and finally read the pixel data. In case the data was padded with zeros we need to read these too and discard the padding data. Some editors will add extra information in the file that we can safely ignore, we just need to adjust header and file size for this. This is necessary in case the user decides to save the processed image.
For saving the image to disk we consider only the 24 and 32 bits per pixel case.
In the 24 bits per pixel case, if the width is divisible by 4, we write the data just like for the 32 bits per pixel case. If the width is not divisible by 4, we increase the row stride, by adding 1 repeatedly, until it is divisible by 4. We fill a padding vector with zeros that will be used at the end of each line. We modify the bitmap headers to take into account the new file size and write the headers like in the previous cases. The data vector is written one row at a time: we write a row, we write the padding data, we write the next row and so on …
Here is the code for writing the image to disk:
At this point, you can read and write a BMP file.
Next, we can write the code for creating a BMP image in memory. For this, like before, we consider only images with 24 and 32 bits per pixel. By default, the image will have 32 bits per pixel, unless the user passes false to the has_alpha parameter. The constructor needs to set the width and height for the image, the header sizes, the file size, the offset data (the position at which the pixel data is written in the file), the bits per pixel count, the compression type and resize the data vector to accommodate the image size:
The only part that remains to be implemented is the modify pixel data part. A quick and dirty approach, without error checking, is to fill a rectangular region from the image with a particular color. For example, we could write:
The above will fill with Red a rectangular region from bmp2.
A better idea is to refactor the above code into a member function of the BMP struct:
Now, we can rewrite the main function:
You can find the complete source code on the GitHub repository for this article.
If you are interested to learn more about modern C++ I would recommend reading A tour of C++ by Bjarne Stroustrup.
Как вывести bmp файл с
Статья открывает целый цикл, посвященный исследованиям различных графических форматов и созданию библиотеки для работы с ними. Весь код будет написан на языке Си, с использованием компилятора GCC. В первой части цикла будет описан формат BMP и реализованы алгоритмы для манипуляции с файлами этого формата. Направление блога не предусматривает применение посторонних библиотек (где это возможно), поэтому вся реализация будет выполнена на как можно более низком уровне. Тем не менее, статья является вводной и лишь поверхностно описывает работу и строение данного формата упуская такие возможности как RLE-кодирование или использование альфа-каналов.
Формат BMP, разработанный компанией Microsoft, является самым простым графическим форматом. Исключая его варианты с использованием сжатия, он представляет собой простую матрицу пикселей. Данный формат подробно описан как на MSDN, так и в Википедии (раздел «Внутреннее строение»). WinAPI имеет свой собственный набор функций для манипуляций с .bmp файлами, но в данной статье он использоваться не будет, так как не позволяет заглянуть внутрь самого формата.
Сам .bmp файл имеет достаточно простое строение. Первые 14 байт отводятся под так называемый BITMAPFILEHEADER (заголовок файла) и имеют следующую структуру:
| поз. | размер (байт) | тип (stdint c-style) | описание |
| 0 | 2 | uint16_t | отметка формата (два ascii символа: «bm») |
| 2 | 4 | uint32_t | размер файла в байтах |
| 6 | 4 | uint32_t | зарезервированное пространство (заполнено нулями) |
| 10 | 4 | uint32_t | положение данных о пикселях относительно начала файла |
Следующие 12-124 байта занимаются информацией о самом BITMAP’e, то есть о пиксельных данных. В WinAPI этот блок называется BITMAPINFO. Как мог заметить внимательный читатель, его размер не фиксирован. В настоящий момент существует четыре возможных размера этого блока: 12, 40, 108 и 124 байта и четыре версии BITMAPINFO соответственно: CORE, 3, 4, 5. От версии к версии изменяется список возможностей и особенностей формата, а также список поддерживаемых версий Windows. Так как данная статья является вводной, в ней не будет подробных описаний версий, а использоваться будет третья (с размером BITMAPINFO 40 байт). Более подробную информацию можно найти в Википедии по ссылке выше.
Структура BITMAPINFO для версии 3:
| поз. | размер (байт.) | тип (stdint c-style) | описание |
| 14 | 4 | uint32_t | размер блока, также указывающий на его версию |
| 18 | 4 | int32_t | ширина изображения (в пикселях) |
| 22 | 4 | int32_t | высота изображения (в пикселях) |
| 26 | 2 | uint16_t | значение формата (для .bmp файлов всегда равно 1) |
| 28 | 2 | uint16_t | количество бит на пиксель |
| 30 | 4 | uint32_t | метод компрессии |
| 34 | 4 | uint32_t | размер пиксельных данных (если не используется сжатие, может быть равно 0) |
| 38 | 4 | int32_t | ppm по горизонтали |
| 42 | 4 | int32_t | ppm по вертикали |
| 46 | 4 | uint32_t | размер таблицы цветов в ячейках |
| 50 | 4 | uint32_t | количество ячеек от начала таблицы цветов до последней используемой (включая её саму) |
После информации о файле и пиксельных данных идут непосредственно сами данные об изображении в формате двумерного массива (если не используется сжатие).
После того, как мы изучили структуру формата, можно приступить к программированию. Нашей задачей будет сохранить массив пикселей в файл и потом считать их из файла в точно такой же массив пикселей. Подготовим структуры для пикселя и для самого изображения, а также функции для упрощения работы с ними (для экономии места в статье простые функции, реализация которых очевидна и не относится к теме статьи, будут объявлены только прототипами, полный код можно скачать по ссылке в конце статьи). Все глобальные переменные, макроопределения, типы данных и функции будут иметь приставку IMPL (аббревиатура от Image ManiPuLation).
Заголовочный файл stdint.h используется для получения доступа к таким типам, как uint8_t, int32_t и другим типам, обеспечивающим контроль над размерами переменных (в отличии от стандартных int и long, размер которых может быть различен на разных компьютерах), что важно, так как данные в файле изображения должны быть структурированы.
Структура IMPL_PIXELMAP будет использоваться для хранения пикселей изображения. Дальше все просто: берем переменные целочисленных типов нужного нам размера, кладем в них данные и записываем в файл. Обратите внимание, что в коде ниже используется 32-х битное представление пикселя (28-й байт), не используется сжатие (30-й байт) и таблица цветов (46-й байт). Хорошая библиотека будет поддерживать все эти функции, но в нашем случае цель скорее в общих чертах изучить формат, чем использовать по максимуму его возможности. Кроме того, обратите внимание на порядок записи байт данных о пикселе (b -> g -> r -> a).
Напишем аналогичную функцию для считывания .bmp файла в IMPL_PIXELMAP. Как и функция сохранения (impl_saveBMP), эта функция далеко не совершенна, например, она не умеет работать с файлами старой (CORE) версии блока BITMAPINFO (в нем под размеры изображения отводится по 16 бит (вместо 32 в версиях 3+), из-за этого данные о количестве бит на пиксель находятся на четыре байта ближе), а еще не поддерживает битности изображения ниже 24-х бит на пиксель и не работает с таблицами цветов. Так или иначе, она отвечает основным требованиям и отлично подходит для демонстрации способа работы с .bmp. При желании, ее легко можно расширить, добавив несколько проверок и при необходимости создав структуры с пикселями нужной битности.
Вот собственно и все, что нужно, для прямой работы с .bmp: стандартная библиотека ввода-вывода да заголовочный файл с целочисленными типами устойчивой размерности. На этом первая часть цикла статей о графических форматах файлов подошла к концу, надеюсь, хоть кому-то она пригодилась. Следующие части будут посвящены куда более сложным форматам, таким как PNG или JPEG, анимациям в GIF, возможно я даже вернусь к BMP и рассмотрю использование RLE-кодирования и добавление альфа-каналов. Спасибо за то, что читаете мой блог.
Writing BMP image in pure c/c++ without other libraries
In my algorithm, I need to create an information output. I need to write a boolean matrix into a bmp file. It must be a monocromic image, where pixels are white if the matrix on such element is true. Main problem is the bmp header and how to write this.
13 Answers 13
See if this works for you. In this code, I had 3 2-dimensional arrays, called red,green and blue. Each one was of size [width][height], and each element corresponded to a pixel — I hope this makes sense!
![]()
Clean C Code for Bitmap (BMP) Image Generation

This code does not use any library other than stdio.h. So, it can be easily incorporated in other languages of C-Family, like- C++, C#, Java.
![]()
Without the use of any other library you can look at the BMP file format. I’ve implemented it in the past and it can be done without too much work.
Bitmap-File Structures
Each bitmap file contains a bitmap-file header, a bitmap-information header, a color table, and an array of bytes that defines the bitmap bits. The file has the following form:
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors[];
BYTE aBitmapBits[];
. see the file format for more details
![]()
Here is a C++ variant of the code that works for me. Note I had to change the size computation to account for the line padding.
Note that the lines are saved from down to up and not the other way around.
Additionally, the scanlines must have a byte-length of multiples of four, you should insert fill bytes at the end of the lines to ensure this.
I just wanted to share an improved version of Minhas Kamal’s code because although it worked well enough for most applications, I had a few issues with it still. Two highly important things to remember:
- The code (at the time of writing) calls free() on two static arrays. This will cause your program to crash. So I commented out those lines.
- NEVER assume that your pixel data’s pitch is always (Width*BytesPerPixel). It’s best to let the user specify the pitch value. Example: when manipulating resources in Direct3D, the RowPitch is never guaranteed to be an even multiple of the byte depth being used. This can cause errors in your generated bitmaps (especially at odd resolutions such as 1366×768).
Below, you can see my revisions to his code:
I edited ralf’s htp code so that it would compile (on gcc, running ubuntu 16.04 lts). It was just a matter of initializing the variables.
The best bitmap encoder is the one you do not write yourself. The file format is a lot more involved, than one might expect. This is evidenced by the fact, that all proposed answers do not create a monochrome (1bpp) bitmap, but rather write out 24bpp files, that happen to only use 2 colors.
The following is a Windows-only solution, using the Windows Imaging Component. It doesn’t rely on any external/3rd party libraries, other than what ships with Windows.
Like every C++ program, we need to include several header files. And link to Windowscodecs.lib while we’re at it:
Next up, we declare our container (a vector, of vectors! Of bool !), and a few smart pointers for convenience:
With that all settled, we can jump right into the implementation. There’s a bit of setup required to get a factory, an encoder, a frame, and get everything prepared:
At that point everything is set up, and we have a frame to dump our data into. For 1bpp files, every byte stores the information of 8 pixels. The left-most pixel is stored in the MSB, with pixels following all the way down to the right-most pixel stored in the LSB.
The code isn’t entirely important; you’ll be replacing that with whatever suits your needs, when you replace the data layout of your input anyway:
What’s left is to commit the changes to the frame and the encoder, which will ultimately write the image file to disk:
This is a test program, writing out an image to a file passed as the first command-line argument:
It produces the following 64×64 image (true 1bpp, 4096 pixels, 574 bytes in size):