Link to home
Start Free TrialLog in
Avatar of Cobras2
Cobras2

asked on

Reading bitmaps in C++

Okay, so I have looked around, read a few tutorials, and discovered ways of reading bitmap files into a DOS app, myself (no I don't want to use MFC or SDL or Windows functions - I'm talking pure C++)
Only problem is none of them work.
My program will load the first member variable of the Bitmap Header struct properly, but after that it gets messed up. I have been messing with it for quite some time but haven't yet been able to figure out whats wrong. I have lots of details I can post if you need them - but all I really want is working code to load the bitmap into memory, or else an eplanation of how C++ loads and saves classes using fstream.write and fstream.read, and how to make it work with the bitmap file.
I am using Windows XP, and have compiled the code under both MSVC++ 6.0 and DJGPP, with the same results.
Avatar of d_iego
d_iego

I think I've got what you need.  Its on codeproject.com it is easy to understand and has examples that work, I hope it works, I'll give you the address:
www.codeproject.com/bitmap/gditutorial.asp


Diego Mendieta
   
 
this page
www.codeproject.com/bitmap/index.asp

is very helpful but in many tutorials (may be all of them) they use MFC. I had the same problem, becuase just like you, I wanted not to use MFC.
Here is a code that writes the bitmap into a file using plain c++ code. The only thing I can give you is a code that writes a bitmap into a file. I know you are looking for a code which reads from a file as well, but this may help you for the writing time.
The program captures the image of the current form and save it in a file (c:\dddd.bmp) in 16-bits. Good luck.


int captura()
{
HWND capture, hw;
char buf[1024];
LPCTSTR tit_ventana;
HDC hdc;

hw = GetForegroundWindow();
     GetWindowText(hw,buf,1023);
     tit_ventana = buf;

if ( (capture = FindWindow(NULL,tit_ventana)) == NULL)
         exit(1);

if ( (hdc = GetWindowDC(capture)) == NULL )  

// get window dimensions
RECT rect;
GetWindowRect(capture, &rect);

size_t dx = rect.right - rect.left;
size_t dy = rect.bottom - rect.top;

// create BITMAPINFO structure
// used by CreateDIBSection
BITMAPINFO info;
info.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth         = dx;
info.bmiHeader.biHeight        = dy;
info.bmiHeader.biPlanes        = 1;
info.bmiHeader.biBitCount      = 16;
info.bmiHeader.biCompression   = BI_RGB;
info.bmiHeader.biSizeImage     = 0;
info.bmiHeader.biXPelsPerMeter = 0;
info.bmiHeader.biYPelsPerMeter = 0;
info.bmiHeader.biClrUsed       = 0;
info.bmiHeader.biClrImportant  = 0;

// a bitmap handle and a pointer its bit data
HBITMAP bitmap = 0;
BYTE*   memory = 0;

// create bitmap
HDC device = GetDC(capture);
bitmap = CreateDIBSection(device, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
ReleaseDC(capture, device);
if(!bitmap || !memory) return 0;

// blit the contents of the desktop (winDC)
// to the bitmap (selected in memDC)
HDC winDC = GetWindowDC(capture);
HDC memDC = CreateCompatibleDC(winDC);
SelectObject(memDC, bitmap);
BitBlt(memDC, 0, 0, dx, dy, winDC, 0, 0, SRCCOPY);
DeleteDC(memDC);
ReleaseDC(capture, winDC);

// create bitmap file
basic_ofstream<char> file("c:\\ddddddd.bmp", ios::binary);
if(!file) { DeleteObject(bitmap); return 0; }

// initialize bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;

fileHeader.bfType      = 0x4d42;
fileHeader.bfSize      = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

infoHeader.biSize          = sizeof(infoHeader);
infoHeader.biWidth         = dx;
infoHeader.biHeight        = dy;
infoHeader.biPlanes        = 1;
infoHeader.biBitCount      = 16;
infoHeader.biCompression   = BI_RGB;
infoHeader.biSizeImage     = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed       = 0;
infoHeader.biClrImportant  = 0;

     // Compute the size of the  infoheader and the color table
 DWORD               dwLen;
     int nColors = (1 << infoHeader.biBitCount);
     if( nColors > 256 )
          nColors = 0;
     dwLen  = infoHeader.biSize + nColors * sizeof(RGBQUAD);


// save file headers
file.write((char*)&fileHeader, sizeof(fileHeader));
file.write((char*)&infoHeader, sizeof(infoHeader));

// save 16-bit bitmap data
int wbytes = (((16*dx + 31) & (~31))/8);
int tbytes = (((16*dx + 31) & (~31))/8)*dy;
file.write((char*)memory, tbytes);
file.close();

// delete bitmap
DeleteObject(bitmap);
bitmap = 0;
memory = 0;


return 0;
}
Avatar of DanRollins
Why your program probably fails is related to the fact that there are several different formats for the data in a BMP file -- specifically the color depth -- BPP (Bits Per Pixel) and that will throw you off if you are not expecting it.

The good news is that tHere is no need to twiddle the bits manually!  To read a .BMP file from disk into memoru, use the Win32 API function
     LoadImage()
You can use that function (and most other Win32 API functions) even if you are running "Plain old DOS code" in a Win32 Console window.  That will not help you in a Unix environment, but you did not specify Unix compatibility as a criteria in your question.

-- Dan
Hi
  If you can wait till 8th (if that is not troubling you), I can give you full turbo C code that displays BMP's in true color (24bpp). It also displays the video card vendor and amount of actual video memory available. It is a DOS based program.

  My home is far away from the place I work, and I visit only in weekends. If you need them, also tell me how to mail you the code (5 kb)

--Vijay
Avatar of Cobras2

ASKER

Actually, most of the above examples use either MFC or the Win32 API.
I said DOS not console :)
I don't want functions that use any Win32 API functions at all, and yes I have accounted for the variations in the way the bitmap structure is stored, however none of it has helped.
It seems that when the program tries to read a structure like this:

struct Bitmap {
unsigned short type;
long Size, Reserved, OffBits;
<..etc>
and the bitmap file itself(look for yourself with a hex editor..) has the following:
2 bytes for type

2 bytes for Size
2 more bytes for Size

2 bytes for Reserved
2 more bytes for Reserved

2 bytes for OffBits
2 more bytes for OffBits

the code reads the first value (unsigned short type) properly, but then assumes that the next 2 bytes are somehow to be disregarded. It ignores them. I have no idea why, but it does, and then picks up reading the file after those two bytes, which are *supposed* to be two of the bytes belonging to (long Size); so what it actually puts into (long Size) are the last two bytes that are supposed to be in it, and then the first two bytes of (long Reserved) !!
this of course messes everything up.
What I need to know is: how can I load it without having this happen?
and also, a much better solution would be, *why* is this happening? I am using the same method that others have used, it worked for them, but it doesn't work for me.
The following links may help you!!!

http://members.fortunecity.com/shetzl/bmpffrmt.html
http://www.wotsit.org/search.asp?s=graphics

www.wotsit.org is great!!
By the way, you must not read the size bytes as 2+2. You must read them as a 4 byte unsigned integer.
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Cobras2

ASKER

DanRollins -
That is *exactly* right on, man :) thanks for the help. I'll tag that as the answer.. however, if it wouldn't be too much trouble, do you have any idea *why* they decided to do that, especially when it affects things like loading bitmaps so much? and why I haven't been able to find any mention of it anywhere before?
Avatar of Cobras2

ASKER

If it wouldn't be too much trouble, could you tell me(if you have any idea) why it is that they changed that? esp. when it makes so much difference to things like bitmap loading? and why I haven't been able to find any mention of that anywhere else? It would really help - but thanks muchly for the answer, man :)
Why add padding?  It is because with most CPUs there is a significant speed improvement in accessing the fields of structures when they are aligned on 32-bit boundaries.  For instance grabbing a 4-byte DWORD that starts at some address like 0x0000081F will require (internal to the CPU) a 1-byte read and a 3-byte read.  If that data happens to align on the edge of an L1 cache bountadry, it (also internally) will require a fetch of a new cache line from the L2 Cache, which could in turn require a fetch from main memory.

Furthermore, other than a few bytes of 'wasted' RAM, there is *normally* no downside -- a program shouldn't care if field 2 is directly adjacent to field 1.  The one exception is in the very case that you hit -- when reading a pre-defined structure directly from disk.

Thanks for the points.
-- Dan

P.S.  Idle curiousity: Why on earth do you want to read BMP files into a DOS program?
Avatar of Cobras2

ASKER

actually the only reason I'm doing it is to get a better base in my skills. I know nobody uses DOS programs anymore, but rather than just doing the abstract calling some BMP-loader in Win32 API, or DirectX, or etc, I thought it would be a good idea to get down and learn how to do it myself.. even if I never actually do that in a real project. basically just a learning experience - and hey, I learned something :)
That makes sense, now that I think of it, that it should be alot easier for the CPU that way, since the modern CPUs are 32bit. I just find it a bit strange that those graphic file format sites wouldn't bother to mention that. I guess maybe they all just haven't been updated in a while?
Anyway, you're welcome for the points, I've been trying to figure this out for awhile now, and although it's not like it's all that important to load bitmaps in DOS, I really did want to know *why* it didn't work. Just bugs me to not ever find the solution to an interesting(even if it's not really worht it) problem ;)
Some example code that reads a BMP might read it a piece at a time.  For instance:  Read two bytes, store a 16-bit word... read one byte, store it... read four bytes, store a DWORD.  That would would work, regardless of alignment padding.   But looking at that code, one might say to oneself "self, that's silly to make three seperate reads... why not just read all seven bytes at once?"  And so it goes. :-)

-- Dan