Jump to content
  • Advertisement
Sign in to follow this  
Bozebo

Making a BMP in c++

This topic is 3079 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

edit:
OK, I fixed the problem. The following code works if 4 is already a factor of the width in bytes, I havn't worked in padding size detection yet. (it just opts for 0 padding and will not work)

#include <stdint.h>
#include <iostream>
//#include <windows.h>

struct fileHeaderMagic{
unsigned char magic[2];
};

struct fileHeader{
uint32_t filesz;
uint16_t creator1;
uint16_t creator2;
uint32_t bmp_offset;
};

typedef struct{
uint32_t header_sz;
uint32_t width;
uint32_t height;
uint16_t nplanes;
uint16_t bitspp;
uint32_t compress_type;
uint32_t bmp_bytesz;
uint32_t hres;
uint32_t vres;
uint32_t ncolors;
uint32_t nimpcolors;
} bmpHeader;

struct colour{
unsigned char b,
g,
r;
};

using namespace std;

//application entry point
int main(){
//bits per pixel
uint16_t bpp = 24;
//file dimensions
uint32_t width = 64,height = 64,
//calculate some required information
pixels = width*height,dataSize;

//make the "magic", file header and bitmap header
fileHeaderMagic file_headerMagic;
fileHeader file_header;
bmpHeader file_bmpHeader;

//make a colour in rbg 24 bit format
colour bmpCol;
bmpCol.r = 0xff;
bmpCol.g = 0;
bmpCol.b = 0;

int16_t padding;
//calculate the size in bytes of the pixels in each row
int16_t pixelRowBytes = width*bpp/8;
cout << "pixelRowBytes: " << pixelRowBytes << endl;
//calculate the padding at the end of a row
if(pixelRowBytes%4 == 0){ //if no padding is required
cout << "no padding required" << endl;
padding = 0;
} else {
cout << "needs padding" << endl;
padding = 0; //bleh
}
//calculate the width of a row in bytes
uint16_t rowBytes = pixelRowBytes + padding;
cout << "rowBytes: " << rowBytes << endl;

//which part of the colour is being written (bgr)
uint16_t colourPart = 0;
//calculate the data size
dataSize = height*rowBytes;
cout << "dataSize: " << dataSize << endl;
//make the bitmap data array
unsigned char bmpData[dataSize];
//fill the data
for(uint16_t row = height;row > 0;row --){ //loop rows, bottom to top order
for(uint16_t col = 0;col < pixelRowBytes;col ++){ //each column with pixels
switch(colourPart){
case 0:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.b; //fill the blue byte
colourPart = 1;
break;
case 1:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.g; //fill the green byte
colourPart = 2;
break;
case 2:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.r; //fill the red byte
colourPart = 0;
break;
}
}
}

//populate the "magic"
file_headerMagic.magic[0] = 0x42; //B
file_headerMagic.magic[1] = 0x4D; //M

//populate the file header
file_header.filesz = sizeof(fileHeaderMagic)+sizeof(fileHeader)+
sizeof(bmpHeader)+dataSize;
file_header.creator1 = 0;
file_header.creator2 = 0;
file_header.bmp_offset = sizeof(fileHeaderMagic)+
sizeof(fileHeader)+sizeof(bmpHeader);

//populate the bitmap header
file_bmpHeader.header_sz = sizeof(bmpHeader);
file_bmpHeader.width = width;
file_bmpHeader.height = height;
file_bmpHeader.nplanes = 1;
file_bmpHeader.bitspp = bpp;
file_bmpHeader.compress_type = 0; //uncompressed
file_bmpHeader.bmp_bytesz = dataSize; //size in bytes of the data
file_bmpHeader.hres = 2835; //pixels per meter, wut
file_bmpHeader.vres = 2835;
file_bmpHeader.ncolors = 0; //no colours in palette
file_bmpHeader.nimpcolors = 0;

//handle for the image file
FILE * image;
//path for the image file
image = fopen("image.bmp","w");

//write the "magic"
fwrite(&file_headerMagic,1,sizeof(fileHeaderMagic),image);
//write the file header
fwrite(&file_header,1,sizeof(fileHeader),image);
//write the bitmap header
fwrite(&file_bmpHeader,1,sizeof(bmpHeader),image);
//write the bitmap data
fwrite(bmpData,1,dataSize,image);
//done with the image
fclose(image);

//wait for keypress
system("pause");
//exit application
return 0;
}



---------------

This should be really straight forward, but I am having problems just generating a bitmap in c++.

First, heres the code:
Removed by OP


That is the latest iteration of my tests, I have been trying to get it to work for ages without success. I just want to make a bitmap of one solid colour, but it seems I can only make a grey filled one; by filling every byte in testData with the same number.

I have tested a lot of different ways, I am starting to think that wikipedia is just wrong.

According to that, for the format I am using, pixels have to be in sets of 2.
Each set of 2 pixels is 64 bits worth of data. I have arranged mine (with my current code) into 2 32 bit unsigned ints.

The idea is, the first int (according to wikipedia) must contain the following bit pattern: bbbbbbbbggggggggrrrrrrrrbbbbbbbb
That being 8 bits for each of the first pixel's bgr components and the last 8 bits for the second pixel's blue component. The second uint32_t consists of: ggggggggrrrrrrrr0000000000000000
That being 8 bits for the second pixel's green and red components, then 16 spare bits that can be anything at all.

As far as I can tell, I haven't been doing anything wrong (my current code might be wrong, but I have been trying for hours with various attempts: none of which seem to make sense)

Can anybody shed any light on the issue? Is wikipedia wrong or am I missing something?

[Edited by - Bozebo on July 8, 2010 4:25:09 PM]

Share this post


Link to post
Share on other sites
Advertisement
I Think i see what your doing, its interesting...but making life hard.

Your are doing something very complex that only needs to be approached if you are using 16 bit depth bitmaps.

For 24 byte bitmaps
Just use an array of unsigned chars to represent the data. One char=one channel (R/G or B).

You will need to take into account padding to 4 byte alignment. So just calculate how many extra bytes you need to round up to 4 and make that the number of bytes per line.

When filling the bitmap remember each line starts at a multiple of the BytesPerRow you just calculated.

Your total bitmap data size will be BytesPerRow*Height. And BytesPerRow will be (Width*3)+Padding

HTH

[Edited by - empirical2 on July 8, 2010 2:46:15 PM]

Share this post


Link to post
Share on other sites
Uhh no, just use 3 bytes for each 24-bit pixel. bit maps also go bottom to top. here's some code of mine where i take a captured video image (YUV) and store it in a 720x480 24-bit Bitmap.


pucBMPptr = pucBitMap;

/* put bitmap header at front */
memcpy(pucBitMap, ku32BMPheader, sizeof(ku32BMPheader));

/* move Bitmap pointer to the end of the BMP data*/
pucBMPptr += (BITMAP_SIZE - 1);

/* convert the YUV image to a RGB image in reverse order */
for(i = 0; i < GraphicsMap.uwHeight; i++)
{
//for (j = 0; j < GraphicsMap.uwWidth/2; j++)
for (j = GraphicsMap.uwWidth/2-1; j >= 0; j--)
{
/* convert YUV to RGB */
//u32YUV = *pu32Image++;

/* go from right to left up lines */
u32YUV = pu32Image[i*(GraphicsMap.uwWidth/2) + j];
u32RGB = YUVtoRGB(u32YUV&0xFF, (u32YUV>>8)&0xFF, (u32YUV>>24)&0xFF);

/* copy the 1st Y with two UV */
/* B, then G, then R (reverse since it's backwards) */
*pucBMPptr-- = (u32RGB >> 16) & 0xFF;
*pucBMPptr-- = (u32RGB >> 8) & 0xFF;
*pucBMPptr-- = u32RGB & 0xFF;

/* copy the 2nd Y with two UV */
u32RGB = YUVtoRGB((u32YUV>>16)&0xFF, (u32YUV>>8)&0xFF, (u32YUV>>24)&0xFF);

*pucBMPptr-- = (u32RGB >> 16) & 0xFF;
*pucBMPptr-- = (u32RGB >> 8) & 0xFF;
*pucBMPptr-- = u32RGB & 0xFF;
}
}



Where ku32BMPheader is:

/* BMP header for a 720x480 24-bit image */
const U32 ku32BMPheader[] =
{0xd2364d42, 0x0000000f, 0x00360000, 0x00280000, 0x02d00000, 0x01e00000,
0x00010000, 0x00000018, 0xd2000000, 0x0ec4000f, 0x0ec40000, 0x00000000,
0x00000000, 0x00000000
};

Share this post


Link to post
Share on other sites
Oops, I completely misinterpreted something important.

I thought that every 2 pixels (48 bits) had a gap of 8 bits afterwards...
I looked at the table of example bitmap and that is just what it seemed like. Must not have been paying attention when I read: "each row of pixels is extended to a 32-bit (4-byte) boundary"

of course the row in the example was 2 pixels wide &_&

edit:
OK, I have it working now.

Edited code in original post, incase someone happens to need some bmp saving source code.

[Edited by - Bozebo on July 8, 2010 4:29:05 PM]

Share this post


Link to post
Share on other sites
Just so you know, that code could be simplified a tad. Something like this?



for(uint16_t row = height;row > 0;row --){ //loop rows, bottom to top order
unsigned char * pData=bmpData+(row*pixelRowBytes-pixelRowBytes);
for(uint16_t col = 0;col < pixelRowBytes/3;col ++){ //each column with pixels
pData[0]= bmpCol.b;
pData[1]= bmpCol.g;
pData[2]= bmpCol.r;
pData+=3;
}
}



(Further room for optimization as well)

Share this post


Link to post
Share on other sites
Quote:
Original post by empirical2 Just so you know, that code could be simplified a tad. Something like this?


Oops, yeah that would have been the obvious solution I don't know why I overcomplicated it so much.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!