My latest attempt(based on several examples online) is failing in a
very strange way(to me).
Here is the code:
<code>
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
filename,
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE|LR_CREATEDIBSECTION|LR_CREATEDIBSECTION);
BITMAP bitmap;
memset(&bitmap, 0, sizeof(BITMAP));
BITMAPINFO bitmapinfo;
memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
HDC hdc = ::GetDC(NULL);
GetDIBits(hdc, hBitmap, 0, bitmap.bmHeight, NULL, &bitmapinfo,
DIB_RGB_COLORS);
int errCode = GetLastError();
</code>
errCode is 8 which translates to:
ERROR_NOT_ENOUGH_MEMORY - Not enough storage is available to process
this command.
I don't see that I am supposed to have allocated anything. From the
examples I have seen online, I should make two calls to GetDIBits();
one to get BITMAPINFO filled and another to get the actual data.
The articles I have seen are littered with information about device
context, resolution, color depth, etc. All I want to do is read the
file and find out what the file's pixel data is so that I can convert
it to HEX codes for an LCD driver for an embedded device.
If anyone could clue me into the *clean* and neat way to read a 1 bit
bitmap and get to the data[] that would be great. I would also like to
know why the above code is failing.
Thank for your time,
Steve
The call to GetDIBits doesn't raise an error code.
It also doesn't set any of the BITMAPINFO struct members :(
So now I have 2 problems...
Well, when using the API GetDIBits(), it is important to handle the
alignment of the DIB data, DIB data are always DWORD aligned, so you
need to pad each scanLine out to a full DWORD boundary.
Total surface bytes = Y Resolution * DWORD aligned X Resolution
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_7gms.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_1rw2.asp
Hope these information helps,
Kellie.
LoadImage will only load bitmaps that have a normal BITMAPINFOHEADER
structure. It will not load BITMAPV4HEADER or BITMAPV5HEADER bitmaps.
#define WORD_ALIGNED_BYTES(width) ((((width-1)>>5)+1)<<2) // WORD aligned
row stride
DWORD dwError;
HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if ( hBitmap ) // assume a bitmap created with CreateDIBSection
{
DIBSECTION ds;
::GetObject( hBitmap, sizeof(ds), &ds );
// allocate a buffer to hold the bits (i.e., row stride * height)
// a 1bpp bitmap requires a WORD aligned buffer
// all other bitmaps require a DWORD aligned buffer
PBYTE lpBits = new BYTE[ WORD_ALIGNED_BYTES(ds.Bm.bmWidth) *
ds.Bm.bmHeight ];
// retrieve the bits... assume lpBits buffer created without error
HDC hDC = ::CreateCompatibleDC(NULL);
int iScanlines = ::GetDIBits( hDC, 0, ds.Bm.bmHeight, lpBits, &ds.Bmih,
DIB_RGB_COLORS);
::DeleteDC(hDC);
if ( iScanlines == ds.Bm.bmHeight )
{
// work with raw bits
delete [] lpBits;
::DeleteObject(hBitmap);
}
else
dwError = ::GetLastError();
}
else
dwError = ::GetLastError();
<Steve...@gmail.com> wrote in message
news:1126025608....@f14g2000cwb.googlegroups.com...
Thank you for the reply. One thing that I have noticed from your code.
DIBSECTION::dsBmih is a BITMAPINFOHEADER and ::GetDIBits() wants
LPBITMAPINFO
I will substitute this sectio of code unless you inform me that I am
missing something. I will let you know how things work out.
Thank you also to Kellie for the additional information.
I'm still getting the code 8 on LoadImage()
The source bitmap I'm testing with has been saved from Photoshop CS and
I also saved a version from MS Paint
Both throw the same error.
I will past the entire code. It's almost EXACTLY what you pasted save
for the error report macro
<code>
#define CheckAndReportIfError(line) \
{ \
DWORD dwError = GetLastError(); \
if(dwError != 0) \
{ \
char buffer[256]={0}; \
sprintf(buffer, "Line: %d\tError: %d", line, dwError); \
MessageBox(0, buffer, "error", MB_OK); \
} \
} \
void GetBitmapBits(CString& filename)
{
#define WORD_ALIGNED_BYTES(width)((((width-1)>>5)+1)<<2) // word
aligned row stride
DWORD dwError;
HBITMAP hBm = (HBITMAP)::LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE|LR_CREATEDIBSECTION);
CheckAndReportIfError(__LINE__);
if(hBm)
{
DIBSECTION ds;
BITMAPINFO bmi;
::GetObject(hBm, sizeof(ds), &ds);
CheckAndReportIfError(__LINE__);
//
// allocate a buffer to hold the bits
//
PBYTE bits = new
BYTE[WORD_ALIGNED_BYTES(ds.dsBm.bmWidth)*ds.dsBm.bmHeight];
ASSERT(bits && "Allocation failed!");
HDC hDC = ::CreateCompatibleDC(NULL);
CheckAndReportIfError(__LINE__);
int iScanlines = ::GetDIBits(hDC, hBm, 0, ds.dsBm.bmHeight, bits,
&bmi/*&ds.dsBmih*/, DIB_RGB_COLORS);
CheckAndReportIfError(__LINE__);
::DeleteDC(hDC);
if(iScanlines == ds.dsBm.bmHeight)
{
//
// work with the bits
//
//
// all done, clean up
//
delete[] bits;
bits = NULL;
::DeleteObject(hBm);
}
else
{
CheckAndReportIfError(__LINE__);
}
}
else
{
CheckAndReportIfError(__LINE__);
}
}
</code>
My code works only for 24bpp or greater since it assumes no
color table. That was my mistake.. sorry about that.
Simply allocate room for the color table:
UINT nColors = 1 << ds.dsBm.BmBits; // should be 2 for 1bpp
LPBITMAPINFO pbmi = (BITMAPINFO*)new
BYTE[sizeof(BITMAPINFOHEADER)+nColors*sizeof(RGBQUAD)];
// copy the BITMAPINFOHEADER from the DIBSECTION
::CopyMemory(pbmi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
// fill in the color table
HDC hDC = ::CreateCompatibleDC(NULL);
// Select the bitmap into the device
HGDIOBJ hOldBitmap = ::SelectObject(hDC, hBitmap);
// retrive the color table from the DIBSection
nColors = ::GetDIBColorTable(hDC, 0, nColors, &pbmi->bmiColors[0]);
::SelectObject(hDC, hOldBitmap);
// Now call GetDIBits with the correct BITMAPINFO pointer
...
delete [] pbmi;
If loadimage is your problem, try loading the bitmap the old fashion
way using file io.
A bitmap consists of a BITMAPFILEHEADER followed by a BITMAPINFOHEADER
followed by the color table followed by the image bits.
bitmap.bmp
BITMAPFILEHEADER - must have signature 0x4D42 ('BM')
BITMAPINFOHEADER
RGBQUADS - one for each color
RAW BITS
<Steve...@gmail.com> wrote in message
news:1126032875.3...@g43g2000cwa.googlegroups.com...
If that fails, try loading the bitmap yourself using file io
and instead of using CreateDIBSection use CreateBitmap because
CreateBitmap was designed for 1bpp monochrome bitmaps.
<Steve...@gmail.com> wrote in message
news:1126033486....@g47g2000cwa.googlegroups.com...
Thanks again for the fast reply. Allocating color table enabled the
GetDIBits to execute. That is a good start ;)
I am now trying to determine if I have valid results, which I'm not
sure about.
I create a 2 pixel x 2 pixel bmp with a black pixel in the top right
corner.
So far, this is what I have observed;
- I've allocated 8 bytes to received the data
- GetDIBits returned 2
- the values of the bytes are: 192, 0, 0, 0, 64, 0, 0, 0
to me, those values don't look correct. I'm trying to see some pattern
or something that is logical, but it looks like junk.
I've never worked with bitmaps like this before, so I'm not sure what
to expect.
Does that look correct to you?
Thanks again!
You can verify this by using simple file io and the info in my
post. You must have a valid BITMAPFILEHEADER with
signature bytes('BM')! <---check with a hex editor
If you don't, then LoadImage can't figure out the correct
memory allocation for the bitmap.
"Michael Phillips, Jr." <mphil...@nospam.jun0.c0m> wrote in message
news:uPZhlsxs...@tk2msftngp13.phx.gbl...
You must unpack each byte to get at the specific
pixel that you want. The pixel will either be set or unset ( 1 or 0 ).
You can use GetPixel( hDC, x, y ) to get the color of the pixel
that you want. Simply select the hBitmap into a device context.
Also, bitmaps are stored upside down.
If you don't want to use GetPixel, you can unpack the bytes
yourself as follows:
// Find the byte on which this scanline begins
DWORD ByteIndex = (bmHeight - y - 1) * WIDTH_ALIGNED_BYTES(bmWidth);
// Find the byte containing this pixel
ByteIndex += (x >> 3);
// Which byte is it?
BYTE BitNumber = (BYTE)( 7 - (x % 8) );
// get the color index
int Index = (lpBits[ByteIndex] << BitNumber) & 0x80 ? 1 : 0;
In the color table you have two colors, selext the matching Index
COLORREF color = RGB(pbmi->bmiColors[Index].rgbRed,
pbmi->bmiColors[Index].rgbGreen, pbmi->bmiColors[Index].rgbBlue);
<Steve...@gmail.com> wrote in message
news:1126036968....@g47g2000cwa.googlegroups.com...
the bfOffBits is 62 and the total size is 70 which leaves 8 for the
data.
Everything looks fine.
if I take a look at the bit data, I see: -64, 0, 0, 0, 64, 0, 0, 0
Which also looks like junk, but like I said, I don't know what I'm
looking for either.
working with the raw data sure is easier than using all the other
methods ;) Granted, it's far from reliable...
A 2x2 bitmap would consist of 8 bytes of raw data
(((2-1)>>5)+1)<<2 = 4 * height(2) = 8 bytes
Your bitmap looks good!
Now you have to unpack the 8 bytes to get at the
individual pixels.
<Steve...@gmail.com> wrote in message
news:1126039170.7...@g47g2000cwa.googlegroups.com...
After looping through and checking each pixel of the 4 pixel bitmap, I
expected to see only 1 of the 4 pixels to be different(my bitmap looks
like this:
[X][ ]
[ ][ ]
Where the X is a black pixel)
But instead, 2 seem to be black. Anyway, I'm stuck(again) and hoped to
paste code so that someone might point out my error.
<code>
void GetBitmapBitsUglyVersion(CString& filename)
{
#define WORD_ALIGNED_BYTES(width)((((width-1)>>5)+1)<<2)
CFile file(filename, CFile::modeRead);
unsigned long long length = file.GetLength();
char* pBuffer = new char[length+1];
char* pDataPtr = pBuffer;
file.Read(pBuffer, length);
file.Close();
PBITMAPFILEHEADER fileHeader = (PBITMAPFILEHEADER)pDataPtr;
pDataPtr += sizeof(BITMAPFILEHEADER);
PBITMAPINFOHEADER infoHeader = (PBITMAPINFOHEADER)pDataPtr;
unsigned int width = infoHeader->biWidth;
unsigned int height = infoHeader->biHeight;
unsigned int bitCount = infoHeader->biBitCount;
unsigned short numPlanes = infoHeader->biPlanes;
pDataPtr+= sizeof(BITMAPINFOHEADER);
pDataPtr+=(numPlanes+1)*sizeof(RGBQUAD); // skip the color table
(it's B&W)
char* pBits = pDataPtr;
char buffer[128];
for(int i=0; i < width; i++)
{
for(int j=0; j < height; j++)
{
// Find the byte on which this scanline begins
DWORD ByteIndex = (height - j - 1) * WORD_ALIGNED_BYTES(width);
// Find the byte containing this pixel
ByteIndex += (i >> 3);
// Which byte is it?
BYTE BitNumber = (BYTE)( 7 - (i % 8) );
// get the color index
int Index = (pBits[ByteIndex] << BitNumber) & 0x80 ? 1 : 0;
if(Index)
{
sprintf(buffer, "x:%d, y:%d is high\r\n", i, j);
OutputDebugString(buffer);
}
}
}
if(pBuffer)
delete[] pBuffer;
}
</code>
Hopefully someone will see the problem and hopefully it will be a basic
one ;)
Thanks for the help!
-Steve
2) You need to read the color table.
When you unpack the byte, you get an index
into the color table that represents the color.
The colors are not mandated to be black and white.
And they don't have to be packed as index[0] = black
and index[1] = white.
3) Your buffer needs to hold WORD_ALIGNED_BYTES(width) * height bytes
For your 2x2 bitmap that's 8 bytes.
4) You loop through each scanline
for ( int i = 0; i < height; i++ ) // 2 scan lines
{
// Find the byte on which this scanline begins
DWORD ByteIndex = (height - i - 1) * WORD_ALIGNED_BYTES(width);
// loop through one WORD aligned scanline
for ( int j = 0; j < WORD_ALIGNED_BYTES(width); j++ ) // rowstride
= 4bytes
for ( int k = 0; k < 8; k++ ) // packed as 8 pixels per byte
{
// locate the bit
BYTE BitNumber = (BYTE)( 7 - (k % 8) );
// get the color index
int Index = (pBits[j] << BitNumber) & 0x80 ? 1 : 0;
// look up the color in the color table
COLORREF color = RGB(pbmi->bmiColors[Index].rgbRed,
pbmi->bmiColors[Index].rgbGreen,
pbmi->bmiColors[Index].rgbBlue);
}
}
<Steve...@gmail.com> wrote in message
news:1126113135.1...@g43g2000cwa.googlegroups.com...
Should be
1) Per the docs the bits are located as follows:
char* pBits = (char*)((PBYTE)fileHeader + fileHeader.bfOffBits);
Sorry about that.
"Michael Phillips, Jr." <mphil...@nospam.jun0.c0m> wrote in message
news:Om0VBd9s...@TK2MSFTNGP15.phx.gbl...
The loop needs to look like this:
for ( int i = 0; i < height; i++ ) // 2 scan lines
{
// Find the byte on which this scanline begins
PBYTE pByte = (PBYTE)pBits + (height - i - 1) *
WORD_ALIGNED_BYTES(width);
// loop through one WORD aligned scanline
for ( int j = 0; j < WORD_ALIGNED_BYTES(width); j++ ) // rowstride
= 4bytes
for ( int k = 0; k < 8; k++ ) // packed as 8 pixels per byte
{
// locate the bit
BYTE BitNumber = (BYTE)( 7 - (k % 8) );
// get the color index
int Index = (pByte[j] << BitNumber) & 0x80 ? 1 : 0;
// look up the color in the color table
COLORREF color = RGB(pbmi->bmiColors[Index].rgbRed,
pbmi->bmiColors[Index].rgbGreen,
pbmi->bmiColors[Index].rgbBlue);
}
}
"Michael Phillips, Jr." <mphil...@nospam.jun0.c0m> wrote in message
news:%23TeSKk9...@TK2MSFTNGP14.phx.gbl...
Now I'm on to review your loop code. I've read the color tables, they
are;
[0] = 0,0,0
[1] = 255,255,255
that looks correct.
Using the loop that you posted, I'm getting indices of 0 and 1 even
when reading an all white 2x2 bitmap.
I haven't been able to find a relationship between the results I'm
seeing.
Once again, for your review, here is the code that I'm using:
<code>
void GetBitmapBitsUglyVersion(CString& filename)
{
#define WORD_ALIGNED_BYTES(width)((((width-1)>>5)+1)<<2)
CFile file(filename, CFile::modeRead);
unsigned long long length = file.GetLength();
char* pBuffer = new char[length+1];
char* pDataPtr = pBuffer;
file.Read(pBuffer, length);
file.Close();
PBITMAPFILEHEADER fileHeader = (PBITMAPFILEHEADER)pDataPtr;
int offset = fileHeader->bfOffBits;
pDataPtr += sizeof(BITMAPFILEHEADER);
PBITMAPINFOHEADER infoHeader = (PBITMAPINFOHEADER)pDataPtr;
unsigned int width = infoHeader->biWidth;
unsigned int height = infoHeader->biHeight;
unsigned int bitCount = infoHeader->biBitCount;
unsigned short numPlanes = infoHeader->biPlanes;
pDataPtr+= sizeof(BITMAPINFOHEADER);
// read the color table
RGBQUAD* colorTables = new RGBQUAD[numPlanes+1];
memcpy(colorTables, pDataPtr, sizeof(RGBQUAD)*(numPlanes+1));
pDataPtr+=(numPlanes+1)*sizeof(RGBQUAD); // skip the color table
(it's B&W)
char* pBits = pDataPtr;
char buffer[128];
int lineCount=0;
for(int i = 0; i < height; i++) // 2 scan lines
{
// Find the byte on which this scanline begins
PBYTE pByte = (PBYTE)pBits + (height - i - 1) *
WORD_ALIGNED_BYTES(width);
// loop through one WORD aligned scanline
for(int j = 0; j < WORD_ALIGNED_BYTES(width); j++) // rowstride=
4bytes
{
for(int k = 0; k < 8; k++) // packed as 8 pixels per byte
{
// locate the bit
BYTE BitNumber = (BYTE)( 7 - (k % 8) );
// get the color index
int Index = (pByte[j] << BitNumber) & 0x80 ? 1 : 0;
// look up the color in the color table
COLORREF color = RGB(colorTables[Index].rgbRed,
colorTables[Index].rgbGreen, colorTables[Index].rgbBlue);
if(color==RGB(0,0,0))
{
// black pixel
lineCount++;
sprintf(buffer, "[%d]\t:x:%d, y:%d is black\r\n", lineCount, i,
j);
OutputDebugString(buffer);
}
}
}
}
if(colorTables)
delete[] colorTables;
if(pBuffer)
delete[] pBuffer;
}
</code>
I will look it over a couple more times and see if I can find the
problem.
Thanks again for your continued involvement, I can't beleive what a
pain this is to solve! ;)
-Steve
No problem. Just changed to following line:
From ------> int Index = (pByte[j] << BitNumber) & 0x80 ? 1 : 0;
To ---------> int Index = (pByte[j] >> BitNumber) & 0x03 ? 1 : 0;
One more correction.
The color table is composed of nColors * sizeof(RGBQUAD)
where nColors = (1 << bitcount), not numPlanes+1.
Here is my code:
<code>
#include <afx.h>
#pragma comment ( lib, "libcmtd.lib")
void GetBitmapBitsUglyVersion(char *filename)
{
#define WORD_ALIGNED_BYTES(width)((((width-1)>>5)+1)<<2)
CFile file(filename, CFile::modeRead);
unsigned long long length = file.GetLength();
char* pBuffer = new char[length+1];
char* pDataPtr = pBuffer;
file.Read(pBuffer, length);
file.Close();
PBITMAPFILEHEADER fileHeader = (PBITMAPFILEHEADER)pDataPtr;
int offset = fileHeader->bfOffBits;
pDataPtr += sizeof(BITMAPFILEHEADER);
PBITMAPINFOHEADER infoHeader = (PBITMAPINFOHEADER)pDataPtr;
unsigned int width = infoHeader->biWidth;
unsigned int height = infoHeader->biHeight;
unsigned int bitCount = infoHeader->biBitCount;
unsigned short numPlanes = infoHeader->biPlanes;
pDataPtr+= sizeof(BITMAPINFOHEADER);
// read the color table
RGBQUAD* colorTables = new RGBQUAD[1 << infoHeader->biBitCount];
memcpy(colorTables, pDataPtr, sizeof(RGBQUAD)*(1 <<
infoHeader->biBitCount));
pDataPtr+=(1 << infoHeader->biBitCount)*sizeof(RGBQUAD); // skip the color
table(it's B&W)
unsigned char* pBits = (PBYTE)fileHeader + offset;
// allocate buffer for 32bpp bitmap
int widthBytes = ((((width + 3)&~3)*32) >> 3);
int imageSize = widthBytes * height;
PBYTE pDibBits = new BYTE[imageSize];
int scanlineWidth = WORD_ALIGNED_BYTES(width);
char buffer[128];
int lineCount=0;
for(int i = 0; i < height; i++) // 2 scan lines
{
// Find the byte on which this scanline begins
char *pByte = (char*)((PBYTE)pBits + (height - i - 1) *scanlineWidth);
DWORD *pdwBits = (DWORD*)((PBYTE)pDibBits + (height - i - 1) *
widthBytes);
int l = 0;
// loop through one WORD aligned scanline
for(int j = 0; j < scanlineWidth; j++) // rowstride=4bytes
{
for(int k = 0; k < 8; k++) // packed as 8 pixels per byte
{
// locate the bit
BYTE BitNumber = (BYTE)( 7 - (k % 8) );
// get the color index
int Index = (pByte[j] >> BitNumber) & 0x03 ? 1 : 0;
// look up the color in the color table
COLORREF color = RGB(colorTables[Index].rgbRed,
colorTables[Index].rgbGreen, colorTables[Index].rgbBlue);
pdwBits[l++] = color;
if(color==RGB(0,0,0))
{
// black pixel
lineCount++;
sprintf(buffer, "[%d]\t:x:%d, y:%d is black\r\n", lineCount, i,j);
OutputDebugString(buffer);
}
}
}
}
if (colorTables)
delete[] colorTables;
if(pBuffer)
delete[] pBuffer;
// save 32bpp bitmap
// Try to open the file for write access
CFile outfile;
outfile.Open("test32bpp.bmp",
CFile::modeReadWrite
| CFile::modeCreate
| CFile::shareExclusive);
BITMAPFILEHEADER bfh;
int sizeofColorTable = 0; //32bpp bitmap
int sizeofImage = imageSize;
// construct the file header
bfh.bfType = 0x4D42; // 'BM'
bfh.bfSize =
sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) +
sizeofColorTable +
sizeofImage;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits =
sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) +
sizeofColorTable;
// write the file header
int iSize = sizeof(bfh);
// write bitmapfileheader
outfile.Write(&bfh, iSize);
iSize = sizeof(BITMAPINFOHEADER) + sizeofColorTable;
infoHeader->biBitCount = 32;
infoHeader->biSizeImage = sizeofImage;
infoHeader->biClrUsed = 0;
infoHeader->biClrImportant = 0;
// write bitmapinfoheader
outfile.Write(infoHeader, iSize);
// write the bits
iSize = sizeofImage;
outfile.Write(pDibBits, iSize);
outfile.Close();
delete [] pDibBits;
}
int main ( int argc, char *argv[] )
{
GetBitmapBitsUglyVersion(argv[2]);
return 0;
}
<code>
I tested the code and it works!
I read in a 1bpp bitmap then unpacked it and created
a 32bpp bitmap.
I saved the bitmap and compared it to the original
in a bitmap viewer.
Both bitmaps look identical except for the bit depth.
<Steve...@gmail.com> wrote in message
news:1126186993....@g43g2000cwa.googlegroups.com...