Scrivere immagini BMP in puro c / c ++ senza altre librerie

Nel mio algoritmo ho bisogno di creare un output di informazioni. Devo scrivere la matrice booleana nel file bmp. Deve essere un’immagine monocromatica, in cui il pixel è bianco se la matrice su tale elemento è vera. Il problema principale è l’intestazione bmp e come scriverlo.

Senza l’uso di altre librerie puoi guardare il formato di file BMP . L’ho implementato in passato e può essere fatto senza troppo lavoro.

Strutture di file bitmap

Ogni file bitmap contiene un’intestazione di file bitmap, un’intestazione di informazioni bitmap, una tabella di colors e una matrice di byte che definisce i bit bitmap. Il file ha il seguente formato:

BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors [];
BYTE aBitmapBits [];

… vedi il formato del file per maggiori dettagli

Vedi se questo funziona per te … In questo codice, avevo 3 matrici bidimensionali, chiamate rosso, verde e blu. Ognuno era di dimensioni [larghezza] [altezza], e ogni elemento corrispondeva a un pixel – spero che questo abbia un senso!

FILE *f; unsigned char *img = NULL; int filesize = 54 + 3*w*h; //w is your image width, h is image height, both int img = (unsigned char *)malloc(3*w*h); memset(img,0,3*w*h); for(int i=0; i 255) r=255; if (g > 255) g=255; if (b > 255) b=255; img[(x+y*w)*3+2] = (unsigned char)(r); img[(x+y*w)*3+1] = (unsigned char)(g); img[(x+y*w)*3+0] = (unsigned char)(b); } } unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0}; unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0}; unsigned char bmppad[3] = {0,0,0}; bmpfileheader[ 2] = (unsigned char)(filesize ); bmpfileheader[ 3] = (unsigned char)(filesize>> 8); bmpfileheader[ 4] = (unsigned char)(filesize>>16); bmpfileheader[ 5] = (unsigned char)(filesize>>24); bmpinfoheader[ 4] = (unsigned char)( w ); bmpinfoheader[ 5] = (unsigned char)( w>> 8); bmpinfoheader[ 6] = (unsigned char)( w>>16); bmpinfoheader[ 7] = (unsigned char)( w>>24); bmpinfoheader[ 8] = (unsigned char)( h ); bmpinfoheader[ 9] = (unsigned char)( h>> 8); bmpinfoheader[10] = (unsigned char)( h>>16); bmpinfoheader[11] = (unsigned char)( h>>24); f = fopen("img.bmp","wb"); fwrite(bmpfileheader,1,14,f); fwrite(bmpinfoheader,1,40,f); for(int i=0; i 

questo è il migliore esempio di basso livello che conosco, scritto da Evercat. copiato da https://en.wikipedia.org/wiki/User:Evercat/Buddhabrot.c

 void drawbmp (char * filename) { unsigned int headers[13]; FILE * outfile; int extrabytes; int paddedsize; int x; int y; int n; int red, green, blue; extrabytes = 4 - ((WIDTH * 3) % 4); // How many bytes of padding to add to each // horizontal line - the size of which must // be a multiple of 4 bytes. if (extrabytes == 4) extrabytes = 0; paddedsize = ((WIDTH * 3) + extrabytes) * HEIGHT; // Headers... // Note that the "BM" identifier in bytes 0 and 1 is NOT included in these "headers". headers[0] = paddedsize + 54; // bfSize (whole file size) headers[1] = 0; // bfReserved (both) headers[2] = 54; // bfOffbits headers[3] = 40; // biSize headers[4] = WIDTH; // biWidth headers[5] = HEIGHT; // biHeight // Would have biPlanes and biBitCount in position 6, but they're shorts. // It's easier to write them out separately (see below) than pretend // they're a single int, especially with endian issues... headers[7] = 0; // biCompression headers[8] = paddedsize; // biSizeImage headers[9] = 0; // biXPelsPerMeter headers[10] = 0; // biYPelsPerMeter headers[11] = 0; // biClrUsed headers[12] = 0; // biClrImportant outfile = fopen(filename, "wb"); // // Headers begin... // When printing ints and shorts, we write out 1 character at a time to avoid endian issues. // fprintf(outfile, "BM"); for (n = 0; n <= 5; n++) { fprintf(outfile, "%c", headers[n] & 0x000000FF); fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8); fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16); fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24); } // These next 4 characters are for the biPlanes and biBitCount fields. fprintf(outfile, "%c", 1); fprintf(outfile, "%c", 0); fprintf(outfile, "%c", 24); fprintf(outfile, "%c", 0); for (n = 7; n <= 12; n++) { fprintf(outfile, "%c", headers[n] & 0x000000FF); fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8); fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16); fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24); } // // Headers done, now write the data... // for (y = HEIGHT - 1; y >= 0; y--) // BMP image format is written from bottom to top... { for (x = 0; x <= WIDTH - 1; x++) { red = reduce(redcount[x][y] + COLOUR_OFFSET) * red_multiplier; green = reduce(greencount[x][y] + COLOUR_OFFSET) * green_multiplier; blue = reduce(bluecount[x][y] + COLOUR_OFFSET) * blue_multiplier; if (red > 255) red = 255; if (red < 0) red = 0; if (green > 255) green = 255; if (green < 0) green = 0; if (blue > 255) blue = 255; if (blue < 0) blue = 0; // Also, it's written in (b,g,r) format... fprintf(outfile, "%c", blue); fprintf(outfile, "%c", green); fprintf(outfile, "%c", red); } if (extrabytes) // See above - BMP lines must be of lengths divisible by 4. { for (n = 1; n <= extrabytes; n++) { fprintf(outfile, "%c", 0); } } } fclose(outfile); return; } drawbmp(filename); 

Nota che le linee vengono salvate dal basso verso l’alto e non viceversa.

Inoltre, le linee di scansione devono avere una lunghezza in byte di multipli di quattro, è necessario inserire i byte di riempimento alla fine delle righe per garantire questo.

Ecco una variante in C ++ del codice che funziona per me. Nota ho dovuto cambiare il calcolo delle dimensioni per tenere conto del riempimento della linea.

 // mimeType = "image/bmp"; unsigned char file[14] = { 'B','M', // magic 0,0,0,0, // size in bytes 0,0, // app data 0,0, // app data 40+14,0,0,0 // start of data offset }; unsigned char info[40] = { 40,0,0,0, // info hd size 0,0,0,0, // width 0,0,0,0, // heigth 1,0, // number color planes 24,0, // bits per pixel 0,0,0,0, // compression is none 0,0,0,0, // image bits size 0x13,0x0B,0,0, // horz resoluition in pixel / m 0x13,0x0B,0,0, // vert resolutions (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) 0,0,0,0, // #colors in pallete 0,0,0,0, // #important colors }; int w=waterfallWidth; int h=waterfallHeight; int padSize = (4-(w*3)%4)%4; int sizeData = w*h*3 + h*padSize; int sizeAll = sizeData + sizeof(file) + sizeof(info); file[ 2] = (unsigned char)( sizeAll ); file[ 3] = (unsigned char)( sizeAll>> 8); file[ 4] = (unsigned char)( sizeAll>>16); file[ 5] = (unsigned char)( sizeAll>>24); info[ 4] = (unsigned char)( w ); info[ 5] = (unsigned char)( w>> 8); info[ 6] = (unsigned char)( w>>16); info[ 7] = (unsigned char)( w>>24); info[ 8] = (unsigned char)( h ); info[ 9] = (unsigned char)( h>> 8); info[10] = (unsigned char)( h>>16); info[11] = (unsigned char)( h>>24); info[20] = (unsigned char)( sizeData ); info[21] = (unsigned char)( sizeData>> 8); info[22] = (unsigned char)( sizeData>>16); info[23] = (unsigned char)( sizeData>>24); stream.write( (char*)file, sizeof(file) ); stream.write( (char*)info, sizeof(info) ); unsigned char pad[3] = {0,0,0}; for ( int y=0; y 255 ) red=255; long green = red; long blue = red; unsigned char pixel[3]; pixel[0] = blue; pixel[1] = green; pixel[2] = red; stream.write( (char*)pixel, 3 ); } stream.write( (char*)pad, padSize ); } 

Pulisci codice C per la generazione di immagini Bitmap (BMP)

Immagine generata:

immagine bitmap


Il codice non utilizza alcuna libreria diversa da stdio.h . Quindi, il codice può essere facilmente incorporato in altre lingue di C-Family, come C ++, C #, Java.


 #include  const int bytesPerPixel = 3; /// red, green, blue const int fileHeaderSize = 14; const int infoHeaderSize = 40; void generateBitmapImage(unsigned char *image, int height, int width, char* imageFileName); unsigned char* createBitmapFileHeader(int height, int width); unsigned char* createBitmapInfoHeader(int height, int width); int main(){ int height = 541; int width = 800; unsigned char image[height][width][bytesPerPixel]; char* imageFileName = "bitmapImage.bmp"; int i, j; for(i=0; i> 8); fileHeader[ 4] = (unsigned char)(fileSize>>16); fileHeader[ 5] = (unsigned char)(fileSize>>24); fileHeader[10] = (unsigned char)(fileHeaderSize + infoHeaderSize); return fileHeader; } unsigned char* createBitmapInfoHeader(int height, int width){ static unsigned char infoHeader[] = { 0,0,0,0, /// header size 0,0,0,0, /// image width 0,0,0,0, /// image height 0,0, /// number of color planes 0,0, /// bits per pixel 0,0,0,0, /// compression 0,0,0,0, /// image size 0,0,0,0, /// horizontal resolution 0,0,0,0, /// vertical resolution 0,0,0,0, /// colors in color table 0,0,0,0, /// important color count }; infoHeader[ 0] = (unsigned char)(infoHeaderSize); infoHeader[ 4] = (unsigned char)(width ); infoHeader[ 5] = (unsigned char)(width>> 8); infoHeader[ 6] = (unsigned char)(width>>16); infoHeader[ 7] = (unsigned char)(width>>24); infoHeader[ 8] = (unsigned char)(height ); infoHeader[ 9] = (unsigned char)(height>> 8); infoHeader[10] = (unsigned char)(height>>16); infoHeader[11] = (unsigned char)(height>>24); infoHeader[12] = (unsigned char)(1); infoHeader[14] = (unsigned char)(bytesPerPixel*8); return infoHeader; } 

Ho modificato il codice htp di ralf in modo che fosse compilato (su gcc, con Ubuntu 16.04 lts). Era solo questione di inizializzare le variabili.

  int w = 100; /* Put here what ever width you want */ int h = 100; /* Put here what ever height you want */ int red[w][h]; int green[w][h]; int blue[w][h]; FILE *f; unsigned char *img = NULL; int filesize = 54 + 3*w*h; //w is your image width, h is image height, both int if( img ) free( img ); img = (unsigned char *)malloc(3*w*h); memset(img,0,sizeof(img)); int x; int y; int r; int g; int b; for(int i=0; i 255) r=255; if (g > 255) g=255; if (b > 255) b=255; img[(x+y*w)*3+2] = (unsigned char)(r); img[(x+y*w)*3+1] = (unsigned char)(g); img[(x+y*w)*3+0] = (unsigned char)(b); } } unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0}; unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0}; unsigned char bmppad[3] = {0,0,0}; bmpfileheader[ 2] = (unsigned char)(filesize ); bmpfileheader[ 3] = (unsigned char)(filesize>> 8); bmpfileheader[ 4] = (unsigned char)(filesize>>16); bmpfileheader[ 5] = (unsigned char)(filesize>>24); bmpinfoheader[ 4] = (unsigned char)( w ); bmpinfoheader[ 5] = (unsigned char)( w>> 8); bmpinfoheader[ 6] = (unsigned char)( w>>16); bmpinfoheader[ 7] = (unsigned char)( w>>24); bmpinfoheader[ 8] = (unsigned char)( h ); bmpinfoheader[ 9] = (unsigned char)( h>> 8); bmpinfoheader[10] = (unsigned char)( h>>16); bmpinfoheader[11] = (unsigned char)( h>>24); f = fopen("img.bmp","wb"); fwrite(bmpfileheader,1,14,f); fwrite(bmpinfoheader,1,40,f); for(int i=0; i 

Se ottieni strani colors cambia nel mezzo dell’immagine usando la funzione C ++ sopra. Assicurati di aprire l’outstream in modalità binaria: imgFile.open(filename, std::ios_base::out | std::ios_base::binary);
Altrimenti, Windows inserisce caratteri indesiderati nel mezzo del tuo file! (mi ha sbattuto la testa per ore)

Vedi domanda correlata qui: Perché ofstream inserisce un byte 0x0D prima di 0x0A?