Ecrire une image BMP en C/c++ PUR sans autres bibliothèques
dans mon algorithme, j'ai besoin de créer une sortie d'information. Je dois écrire la matrice booléenne dans le fichier bmp. Il doit s'agir d'une image monocrome, où le pixel est blanc si la matrice sur un tel élément est vraie. Principal problème est bmp en-tête et comment l'écrire.
8 réponses
sans l'utilisation de toute autre bibliothèque, vous pouvez regarder le format de fichier BMP . Je l'ai mis en œuvre dans le passé et cela peut se faire sans trop de travail.
Bitmap-Structures De Fichier
chaque fichier bitmap contient un bitmap-en-tête de fichier, a bitmap-en-tête d'information, une couleur table, et un tableau d'octets définit les bits bitmap. Le fichier a le formulaire suivant:
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
Rgbquad aColors [];
BYTE aBitmapBits [];
... voir le format de fichier pour plus de détails
Voyez si cela fonctionne pour vous... Dans ce code, j'avais 3 tableaux en 2 dimensions, appelés rouge,vert et bleu. Chacun était de taille [largeur][hauteur], et chaque élément correspond à un pixel - j'espère que cela a du sens!
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<w; i++)
{
for(int j=0; j<h; j++)
{
x=i; y=(h-1)-j;
r = red[i][j]*255;
g = green[i][j]*255;
b = blue[i][j]*255;
if (r > 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<h; i++)
{
fwrite(img+(w*(h-i-1)*3),3,w,f);
fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}
free(img);
fclose(f);
c'est le meilleur exemple de bas niveau que je connaisse, écrit par Evercat. copié à partir de 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);
notez que les lignes sont sauvegardées de bas en haut et non l'inverse.
de plus, les lignes de balayage doivent avoir un byte-longueur de multiples de quatre, vous devez insérer des octets de remplissage à la fin des lignes pour assurer cela.
Voici une variante C++ du code qui fonctionne pour moi. Remarque, j'ai dû changer la taille de calcul pour tenir compte de la ligne de remplissage.
// 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<h; y++ )
{
for ( int x=0; x<w; x++ )
{
long red = lround( 255.0 * waterfall[x][y] );
if ( red < 0 ) red=0;
if ( red > 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 );
}
Clean C Code for Bitmap (BMP) Image Generation""
Image Générée:
le code n'utilise pas de bibliothèque autre que stdio.h . Donc, le code peut être facilement incorporé dans d'autres langues de C, C++, C#, Java.
#include <stdio.h>
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<height; i++){
for(j=0; j<width; j++){
image[i][j][2] = (unsigned char)((double)i/height*255); ///red
image[i][j][1] = (unsigned char)((double)j/width*255); ///green
image[i][j][0] = (unsigned char)(((double)i+j)/(height+width)*255); ///blue
}
}
generateBitmapImage((unsigned char *)image, height, width, imageFileName);
}
void generateBitmapImage(unsigned char *image, int height, int width, char* imageFileName){
unsigned char* fileHeader = createBitmapFileHeader(height, width);
unsigned char* infoHeader = createBitmapInfoHeader(height, width);
unsigned char padding[3] = {0, 0, 0};
int paddingSize = (4-(width*bytesPerPixel)%4)%4;
FILE* imageFile = fopen(imageFileName, "wb");
fwrite(fileHeader, 1, fileHeaderSize, imageFile);
fwrite(infoHeader, 1, infoHeaderSize, imageFile);
int i;
for(i=0; i<height; i++){
fwrite(image+(i*width*bytesPerPixel), bytesPerPixel, width, imageFile);
fwrite(padding, 1, paddingSize, imageFile);
}
fclose(imageFile);
}
unsigned char* createBitmapFileHeader(int height, int width){
int fileSize = fileHeaderSize + infoHeaderSize + bytesPerPixel*height*width;
static unsigned char fileHeader[] = {
0,0, /// signature
0,0,0,0, /// image file size in bytes
0,0,0,0, /// reserved
0,0,0,0, /// start of pixel array
};
fileHeader[ 0] = (unsigned char)('B');
fileHeader[ 1] = (unsigned char)('M');
fileHeader[ 2] = (unsigned char)(fileSize );
fileHeader[ 3] = (unsigned char)(fileSize>> 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;
}
j'ai modifié le code HTP DE ralf pour qu'il soit compilé (sur gcc, avec ubuntu 16.04 lts). C'était juste une question d'initialisation des variables.
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<w; i++)
{
for(int j=0; j<h; j++)
{
x=i; y=(h-1)-j;
r = red[i][j]*255;
g = green[i][j]*255;
b = blue[i][j]*255;
if (r > 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<h; i++)
{
fwrite(img+(w*(h-i-1)*3),3,w,f);
fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}
fclose(f);
si vous recevez des changements de couleurs étranges au milieu de votre image en utilisant la fonction C++ ci-dessus. Assurez-vous d'ouvrir l'outstream en mode binaire:
imgFile.open(filename, std::ios_base::out | std::ios_base::binary);
Sinon windows insère des caractères indésirables au milieu de votre fichier! (à se taper la tête sur ce problème pendant des heures)
voir la question connexe ici: pourquoi ofstream insère-t-il un octet 0x0D avant 0x0A?