xcdemon05
xcdemon05

Reputation: 1422

Creating a bitmap from raw headers and image data

I am extremely new to manipulating bitmap data in C++, and I have a problem. I'm trying to follow this example from wikipedia. Here is the code I'm using:

#include <iostream>
#include <fstream>
#include <Windows.h>

using namespace std;

int main()
{

    //fileheader
    BITMAPFILEHEADER* bf = new BITMAPFILEHEADER;
    bf->bfType = 66;
    bf->bfSize = 70;
    bf->bfOffBits = 54;

    //infoheader
    BITMAPINFOHEADER* bi = new BITMAPINFOHEADER;
    bi->biSize = 40;
    bi->biWidth = 2;
    bi->biHeight = 2;
    bi->biPlanes = 1;
    bi->biBitCount = 24;
    bi->biCompression = 0;
    bi->biSizeImage = 16;
    bi->biXPelsPerMeter = 2835;
    bi->biYPelsPerMeter = 2835;
    bi->biClrUsed = 0;
    bi->biClrImportant = 0;

    //image data
    unsigned char* thedata = new unsigned char;
    thedata[0] = 0;
    thedata[1] = 0;
    thedata[2] = 255;

    thedata[3] = 255;
    thedata[4] = 255;
    thedata[5] = 255;

    thedata[6] = 0;
    thedata[7] = 0;

    thedata[8] = 255;
    thedata[9] = 0;
    thedata[10] = 0;

    thedata[11] = 0;
    thedata[12] = 255;
    thedata[13] = 0;

    thedata[14] = 0;
    thedata[15] = 0;

    //dc
    HDC dc = GetDC(NULL);

    //bitmap info
    BITMAPINFO* bmi = (BITMAPINFO*)bi;

    //handle to bitmap
    HBITMAP hbmp = CreateDIBitmap(dc, bi, CBM_INIT, thedata, bmi, DIB_RGB_COLORS);

    //output to bmp....?
    ofstream outFile;
    outFile.open("outtestbmp.bmp");
    outFile << hbmp;
    outFile.close();

}

I've been searching Google for the past couple days trying to figure out how to get this done but I still can't seem to make it work.

This complies without errors, but the outtestbmp.bmp file in the end is unopenable. Are there any huge mistakes I'm making (probably dozens) that are preventing this from working? (I have high suspicions that using ofstream to output my bmp data is wrong).

EDIT: I've been told that setting bftype to 66 is wrong. What is the correct value?

Also, I've created a .bmp file of what the output should be. Here is the hex data for that bmp:

42 4D 46 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 02 00 00 00 02 00 00 00 01 00 18        
00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF  
FF FF 00 00 FF 00 00 00 FF 00 00 00

and here is the data for my .bmp I'm outputting:

42 00 46 00 00 00 CD CD CD CD 36 00 00 00 28 00 00 00 02 00 00 00 02 00 00 00 01 00 18 
00 00 00 00 00 10 00 00 00 13 0B 00 00 13 0B 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 
FF FF 00 00 FF 00 00 00 FF 00 00 00

Upvotes: 0

Views: 4298

Answers (4)

JasonD
JasonD

Reputation: 16582

bf->bfType == 66;

This is wrong on two counts. Firstly it's the wrong value (the magic value are the bytes 'BM' or 0x4d42 on a little-endian machine), and secondly it's not an assignment.

Then:

unsigned char* thedata = new unsigned char;

Only allocating 1 char?

And this:

outFile << hbmp;

Doesn't do what you think it does. You're just writing out a handle. You didn't need to create a bitmap, you just need to write out the data structures you've initialised (once you've made them correct).

And why are you new'ing everything? There's no need for this stuff to be dynamically allocated (and you're not delete'ing it either).

I would generally do it something like this (some code removed for brevity):

BITMAPFILEHEADER bf;
bf.bfType = 0x4d42;
bf.bfSize = 70;
bf.bfOffBits = 54;

//infoheader
BITMAPINFOHEADER bi;
bi.biSize = 40;
bi.biWidth = 2;
etc...

//image data
unsigned char* thedata = new unsigned char[16]; // Should really calculate this properly
thedata[0] = 0;
....

//output to bmp....?
ofstream outFile;
outFile.open("outtestbmp.bmp");
outFile.write((char *)&bf,sizeof(bf));
outFile.write((char *)&bi,sizeof(bi));
outFile.write((char *)thedata, 16);
outFile.close();

Upvotes: 3

Mats Petersson
Mats Petersson

Reputation: 129314

First, you need to allocate enough data for your pixels:

unsigned char* thedata = new unsigned char[2 * 2 * 3];

Then you need to use write and open the file as binary.

Instead of:

outFile.open("outtestbmp.bmp");
outFile << hbmp;

Something like:

outFile.open("outtestbmp.bmp", ios::binary||ios::out);

outfile.write(reinterpret_cast<char *>(bf), sizeof(BITMAPFILEHEADER));
outfile.write(reinterpret_cast<char *>(bi), sizeof(BITMAPINFOHEADER));
outfile.write(reinterpret_cast<char *>(thedata), 2 * 2 * 3); 

outfile.close();

Upvotes: 0

NtscCobalt
NtscCobalt

Reputation: 1689

HBITMAP is a handle to a bitmap not an actual bitmap. The real bitmap is stored in Kernel memory. You don't need to make any calls to the GDI if you are just writing the bitmap to a file. Even if it was an actual bitmap cout would need an operator overload to actually write the memory structure to a file since it isn't stored in memory the same it is on disk.

All you need to do is write the BITMAPFILEHEADER to a file, then write the BITMAPINFOHEADER to the file, and then write the data (if we are talking RGB, non-indexed).

Here is more info on how bitmaps are actually stored on disk.

Bitmap Storage (MSDN)

Reading a bitmap from a file is just same.

Upvotes: 1

emartel
emartel

Reputation: 7773

First you don't allocate enough memory for all your pixels, it should be something along the lines of

unsigned char* thedata = new unsigned char[numPixels * bytesPerPixel];

In your case, with a 2 by 2 picture and 24 bpp (bits per pixel), you should allocate 12 bytes.

Each pixel will correspond to three bytes in a row for their red, green and blue channel.

Note: I've never used Window's bitmap creation process but I worked with many libraries to manipulate images, including FreeImage, SDL and GD

Upvotes: 1

Related Questions