I've rewritten and commented the answer from https://mcmap.net/q/319859/-writing-bmp-image-in-pure-c-c-without-other-libraries. I hope you find it clear enough.
#include <cstddef>
#include <armadillo>
#include <map>
#include <cstdio>
#include <cassert>
///Just a tiny struct to bundle three values in range [0-255].
struct Color{
Color(unsigned char red, unsigned char green, unsigned char blue)
: red(red),green(green),blue(blue)
{}
///Defualt constructed Color() is black.
Color()
: red(0),green(0),blue(0)
{}
///Each color is represented by a combination of red, green, and blue.
unsigned char red,green,blue;
};
int main(int argc,char**argv)
{
///The width of the image. Replace with your own.
std::size_t w = 7;
///The height of the image. Replace with your own
std::size_t h = 8;
///http://arma.sourceforge.net/docs.html#Mat
///The Armadillo Linear Algebra Library Mat constructor is of the following
/// signature: mat(n_rows, n_cols).
arma::Mat<int> intmatrix(h,w);
///Fill out matrix, replace this with your own.
{
///Zero fill matrix
for(std::size_t i=0; i<h; ++i)
for(std::size_t j=0;j<w; ++j)
intmatrix(i,j) = 0;
intmatrix(0,3) = 1;
intmatrix(1,3) = 1;
intmatrix(2,2) = 6;
intmatrix(2,4) = 6;
intmatrix(3,2) = 4;
intmatrix(3,4) = 4;
intmatrix(4,1) = 6;
intmatrix(4,2) = 6;
intmatrix(4,3) = 6;
intmatrix(4,4) = 6;
intmatrix(4,5) = 6;
intmatrix(5,1) = 1;
intmatrix(5,2) = 1;
intmatrix(5,3) = 1;
intmatrix(5,4) = 1;
intmatrix(5,5) = 1;
intmatrix(6,0) = 4;
intmatrix(6,6) = 4;
intmatrix(7,0) = 6;
intmatrix(7,6) = 6;
}
///Integer to color associations. This is a map
///that records the meanings of the integers in the matrix.
///It associates a color with each integer.
std::map<int,Color> int2color;
///Fill out the color associations. Replace this with your own associations.
{
///When we see 0 in the matrix, we will use this color (red-ish).
int2color[0] = Color(255,0,0);
///When we see 0 in the matrix, we will use this color (green-ish).
int2color[1] = Color(0,255,0);
///When we see 0 in the matrix, we will use this color (blue-ish).
int2color[4] = Color(0,0,255);
///When we see 0 in the matrix, we will use this color (grey-ish).
int2color[6] = Color(60,60,60);
}
///The file size will consist of w*h pixels, each pixel will have an RGB,
/// where each color R,G,B is 1 byte, making the data part of the file to
/// be of size 3*w*h. In addition there is a header to the file which will
/// take of 54 bytes as we will see.
std::size_t filesize = 54 + 3*w*h;
///We make an array of 14 bytes to represent one part of the header.
///It is filled out with some default values, and we will fill in the
///rest momentarily.
unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
///The second part of the header is 40 bytes; again we fill it with some
///default values, and will fill in the rest soon.
unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
///We will now store the filesize,w,h into the header.
///We can't just write them to the file directly, because different platforms
///encode their integers in different ways. This is called "endianness"
///or "byte order". So we chop our integers up into bytes, and put them into
///the header byte-by-byte in the way we need to.
///Encode the least significant 8 bits of filesize into this byte.
///Because sizeof(unsigned char) is one byte, and one byte is eight bits,
///when filesize is casted to (unsigned char) only the least significant
///8 bits are kept and stored into the byte.
bmpfileheader[ 2] = (unsigned char)(filesize );
///... Now we shift filesize to the right 1 byte, meaning and trunctate
///that to its least significant 8 bits. This gets stored in the next
///byte.
bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
///...
bmpfileheader[ 4] = (unsigned char)(filesize>>16);
///Encodes the most significant 8 bits of filesize into this byte.
bmpfileheader[ 5] = (unsigned char)(filesize>>24);
///Now we will store w (the width of the image) in the same way,
/// but into the byte [5-8] in bmpinfoheader.
bmpinfoheader[ 4] = (unsigned char)( w );
bmpinfoheader[ 5] = (unsigned char)( w>> 8);
bmpinfoheader[ 6] = (unsigned char)( w>>16);
bmpinfoheader[ 7] = (unsigned char)( w>>24);
///Now we will store h (the width of the image) in the same way,
/// but into the byte [9-12] in bmpinfoheader.
bmpinfoheader[ 8] = (unsigned char)( h );
bmpinfoheader[ 9] = (unsigned char)( h>> 8);
bmpinfoheader[10] = (unsigned char)( h>>16);
bmpinfoheader[11] = (unsigned char)( h>>24);
///Now we open the output file
FILE* f = fopen("img.bmp","wb");
///First write the bmpfileheader to the file. It is 14 bytes.
///The 1 means we are writing 14 elements of size 1.
///Remember, bmpfileheader is an array which is basically
///the same thing as saying it is a pointer to the first element
///in an array of contiguous elements. We can thus say:
///write 14 bytes, starting from the spot where bmpfileheader points
///to.
fwrite(bmpfileheader,1,14,f);
///Then write the bmpinfoheader, which is 40 bytes, in the same way.
fwrite(bmpinfoheader,1,40,f);
///Now we write the data.
///For each row (there are h rows), starting from the last, going
///up to the first.
///We iterate through the rows in reverse order here,
///apparently in the BMP format, the image
///is stored upside down.
for(std::size_t i=h-1; i != std::size_t(-1); --i)
{
///For each column in the row,
for(std::size_t j=0; j<w; ++j)
{
///We retreive the integer of the matrix at (i,j),
///and assert that there is a color defined for it.
assert (int2color.count(intmatrix(i,j)) != 0
&& "Integer in matrix not defined in int2color map");
///We somehow get the color for pixel (i,j).
///In our case, we get it from the intmatrix, and looking
///up the integer's color.
Color color = int2color[intmatrix(i,j)];
///Now the colors are written in reverse order: BGR
///We write the color using fwrite, by taking a pointer
///of the (unsigned char), which is the same thing as
///an array of length 1. Then we write the byte.
///First for blue,
fwrite(&color.blue,1,1,f);
///Same for green,
fwrite(&color.green,1,1,f);
///Finally red.
fwrite(&color.red,1,1,f);
}
///Now we do some padding, from 0-3 bytes, depending in the width.
unsigned char bmppad[3] = {0,0,0};
fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}
///Free the file.
fclose(f);
return 0;
}