flynny
asked on
help creating monochrome bitmap from existing bitmap
Hi
I'm trying to wrtie a method that will highlight an images. i.e. set al the pixels of a set rgb to be black and all others to be white. I then want to return this as HBITMAP.
its almost working but i can't work out where i'm going wrong and hope you can help me.
i create the bitmap like so
BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER *) malloc(sizeof(BITMAPFILEHE ADER));
bfh->bfType='B'+('M'<<8);
bfh->bfOffBits= sizeof(BITMAPFILEHEADER)+s izeof(BITM APINFOHEAD ER) + 8; //offset to bitmap pixel data + 8 is for the two colour bytes
bfh->bfSize= paddedSize + bfh->bfOffBits;
bfh->bfReserved1=0;
bfh->bfReserved2=0;
BITMAPINFOHEADER* Bmi = (BITMAPINFOHEADER *) malloc(sizeof(BITMAPINFOHE ADER));
Bmi->biSize = sizeof(BITMAPINFOHEADER);
Bmi->biWidth = WorkBmp.bmWidth; //WorkBmp is the orginal BITMAP
Bmi->biHeight = WorkBmp.bmHeight;
Bmi->biPlanes = 1;
Bmi->biBitCount = 1;
Bmi->biCompression = BI_RGB; //BI_RGB=0
Bmi->biSizeImage = paddedSize;
Bmi->biXPelsPerMeter = 0;
Bmi->biYPelsPerMeter = 0;
Bmi->biClrUsed = 0;
Bmi->biClrImportant = 0;
where paddedSize
int paddedSize = WorkBmp.bmHeight * ( WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4)));
RGBQUAD colors[2];
colors[0].rgbBlue = colors[0].rgbGreen = colors[0].rgbRed = 0;
colors[1].rgbBlue = colors[1].rgbGreen = colors[1].rgbRed = 255;
colors[0].rgbReserved = colors[1].rgbReserved = 0;
HDC hdc = GetDC(NULL);
LPBITMAPINFO bi = (LPBITMAPINFO) malloc(sizeof(LPBITMAPINFO ));
bi->bmiHeader = *Bmi;
bi->bmiColors[0] = colors[0];
bi->bmiColors[1] = colors[1];
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)pBmiSrc, NULL, 0);
ReleaseDC(NULL, hdc);
to write the image to a file i use
CBitmap* cBitmap = CBitmap::FromHandle(highli ghtedBitma p);
CPalette cPal;
HANDLE bHandle = DDBToDIB(*cBitmap, BI_RGB ,&cPal);
BOOL writtenOK = WriteDIB(outstr, bHandle);
where DDBToDIB and WriteDIB were pulled from the web and i have tesed them and they have worked for me in the past.
however the problem is it simpy creates black images if i write it to a file using the returned HBITMAP. I know that pBmiSrc, the bit array, is correct as i have tried manually adding its contents into a hex editor and the bitmap is then appears as it should.
I'm trying to wrtie a method that will highlight an images. i.e. set al the pixels of a set rgb to be black and all others to be white. I then want to return this as HBITMAP.
its almost working but i can't work out where i'm going wrong and hope you can help me.
i create the bitmap like so
BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER *) malloc(sizeof(BITMAPFILEHE
bfh->bfType='B'+('M'<<8);
bfh->bfOffBits= sizeof(BITMAPFILEHEADER)+s
bfh->bfSize= paddedSize + bfh->bfOffBits;
bfh->bfReserved1=0;
bfh->bfReserved2=0;
BITMAPINFOHEADER* Bmi = (BITMAPINFOHEADER *) malloc(sizeof(BITMAPINFOHE
Bmi->biSize = sizeof(BITMAPINFOHEADER);
Bmi->biWidth = WorkBmp.bmWidth; //WorkBmp is the orginal BITMAP
Bmi->biHeight = WorkBmp.bmHeight;
Bmi->biPlanes = 1;
Bmi->biBitCount = 1;
Bmi->biCompression = BI_RGB; //BI_RGB=0
Bmi->biSizeImage = paddedSize;
Bmi->biXPelsPerMeter = 0;
Bmi->biYPelsPerMeter = 0;
Bmi->biClrUsed = 0;
Bmi->biClrImportant = 0;
where paddedSize
int paddedSize = WorkBmp.bmHeight * ( WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4)));
RGBQUAD colors[2];
colors[0].rgbBlue = colors[0].rgbGreen = colors[0].rgbRed = 0;
colors[1].rgbBlue = colors[1].rgbGreen = colors[1].rgbRed = 255;
colors[0].rgbReserved = colors[1].rgbReserved = 0;
HDC hdc = GetDC(NULL);
LPBITMAPINFO bi = (LPBITMAPINFO) malloc(sizeof(LPBITMAPINFO
bi->bmiHeader = *Bmi;
bi->bmiColors[0] = colors[0];
bi->bmiColors[1] = colors[1];
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)pBmiSrc, NULL, 0);
ReleaseDC(NULL, hdc);
to write the image to a file i use
CBitmap* cBitmap = CBitmap::FromHandle(highli
CPalette cPal;
HANDLE bHandle = DDBToDIB(*cBitmap, BI_RGB ,&cPal);
BOOL writtenOK = WriteDIB(outstr, bHandle);
where DDBToDIB and WriteDIB were pulled from the web and i have tesed them and they have worked for me in the past.
however the problem is it simpy creates black images if i write it to a file using the returned HBITMAP. I know that pBmiSrc, the bit array, is correct as i have tried manually adding its contents into a hex editor and the bitmap is then appears as it should.
I think that "hBitmap" is already a DIB, so you can pass it directly to WriteDIB() without using CBitmap and DDBToDIB:
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)pBmiSrc, NULL, 0);
BOOL writtenOK = WriteDIB(outstr, hBitmap );
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)pBmiSrc, NULL, 0);
BOOL writtenOK = WriteDIB(outstr, hBitmap );
ASKER
hi guys thanks for the replies.
yes i think the problem looks like it could be in the LPBITMAPINFO as if i do the following it writes the bitmap ok
FILE *p = fopen( "c:\\test12345.bmp", "wb" );
fwrite( bfh, sizeof(BITMAPFILEHEADER), 1, p );
fwrite( Bmi, sizeof(BITMAPINFOHEADER), 1, p );
fwrite( &colors[0],4, 1, p );
fwrite( &colors[1],4, 1, p );
fwrite( pBmiSrc,Bmi->biSizeImage, 1, p );
fclose( p );
like you said alb66 i tried passing the hbitmap to theWrteDIB method and it doesnt appear to be writing the hbitmap
i create and pass as follows
BOOL writtenOK = WriteDIB("c:\\test12345.bm p", hBitmap );
and it seems to fail here
int nColors = 1 << lpbi->biBitCount;
i assume because its not been able to cast hDIB to a LPBITMAPINFOHEADER? any ideas why it does this?
yes i think the problem looks like it could be in the LPBITMAPINFO as if i do the following it writes the bitmap ok
FILE *p = fopen( "c:\\test12345.bmp", "wb" );
fwrite( bfh, sizeof(BITMAPFILEHEADER), 1, p );
fwrite( Bmi, sizeof(BITMAPINFOHEADER), 1, p );
fwrite( &colors[0],4, 1, p );
fwrite( &colors[1],4, 1, p );
fwrite( pBmiSrc,Bmi->biSizeImage, 1, p );
fclose( p );
like you said alb66 i tried passing the hbitmap to theWrteDIB method and it doesnt appear to be writing the hbitmap
i create and pass as follows
BOOL writtenOK = WriteDIB("c:\\test12345.bm
and it seems to fail here
int nColors = 1 << lpbi->biBitCount;
i assume because its not been able to cast hDIB to a LPBITMAPINFOHEADER? any ideas why it does this?
BOOL CBitmapOCR::WriteDIB( LPTSTR szFile, HANDLE hDIB)
{
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbi;
if (!hDIB)
return FALSE;
CFile file;
if( !file.Open( szFile, CFile::modeWrite|CFile::modeCreate) )
return FALSE;
lpbi = (LPBITMAPINFOHEADER)hDIB;
int nColors = 1 << lpbi->biBitCount;
// Fill in the fields of the file header
hdr.bfType = ((WORD) ('M' << 8) | 'B'); // is always "BM"
hdr.bfSize = GlobalSize (hDIB) + sizeof( hdr );
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = (DWORD) (sizeof( hdr ) + lpbi->biSize +
nColors * sizeof(RGBQUAD));
// Write the file header
file.Write( &hdr, sizeof(hdr) );
// Write the DIB header and the bits
file.Write( lpbi, GlobalSize(hDIB) );
return TRUE;
}
> and it seems to fail here
what do you mean with it fails? does it crash?
what do you mean with it fails? does it crash?
ASKER
yes it crashes after
lpbi = (LPBITMAPINFOHEADER)hDIB;
once i try and access any variable in the LPBITMAPINFOHEADER. I read somewhere that i will need to convert the HBitmap to a handle by globalalloc-ing the memory. I tried this but it didn't seem to work using the following
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)&pBmiSrc, NULL, 0);
HGLOBAL memPtr = GlobalAlloc(GMEM_FIXED, sizeof(hBitmap)); //pointer to mem block
HBITMAP tmpBitmap;
if( memPtr && (tmpBitmap = (HBITMAP)GlobalLock(memPtr )) != NULL )
tmpBitmap = hBitmap;
BOOL writtenOK = WriteDIB("c:\\test12345.bm p", tmpBitmap);
this just did exactly the same thing.
lpbi = (LPBITMAPINFOHEADER)hDIB;
once i try and access any variable in the LPBITMAPINFOHEADER. I read somewhere that i will need to convert the HBitmap to a handle by globalalloc-ing the memory. I tried this but it didn't seem to work using the following
HBITMAP hBitmap = ::CreateDIBSection(hdc, (LPBITMAPINFO)bi, DIB_RGB_COLORS, (LPVOID *)&pBmiSrc, NULL, 0);
HGLOBAL memPtr = GlobalAlloc(GMEM_FIXED, sizeof(hBitmap)); //pointer to mem block
HBITMAP tmpBitmap;
if( memPtr && (tmpBitmap = (HBITMAP)GlobalLock(memPtr
tmpBitmap = hBitmap;
BOOL writtenOK = WriteDIB("c:\\test12345.bm
this just did exactly the same thing.
>>>> lpbi = (LPBITMAPINFOHEADER)hDIB;
The hDIB is a handle to a bitmap. It is some kind of 'id' used from Windows to identify its resources. Though most likely there is a association from the handle to some structure, the handle itself is not a pointer and therefore it crashes.
Normally, you have a HBITMAP handle to a DDB (a device-dependent-bitmap where the device is the screen) and you call GetDIBits to get the bits from that bitmap.
You should check the link I gave you above. There is full source code for exactly the task you asked for.
The hDIB is a handle to a bitmap. It is some kind of 'id' used from Windows to identify its resources. Though most likely there is a association from the handle to some structure, the handle itself is not a pointer and therefore it crashes.
Normally, you have a HBITMAP handle to a DDB (a device-dependent-bitmap where the device is the screen) and you call GetDIBits to get the bits from that bitmap.
You should check the link I gave you above. There is full source code for exactly the task you asked for.
ASKER
hi itsme, sorry i did mention the link in my first reply but unfortunately the ie crashed and so had to retype and in my rush forgot to readd about this. ;)
The difference between the link and what i want to do is that the link will simply convert the image to monochrome and i have no control upon which pixel/s will be black and which will be white (for example in theory i woulld like to be able to specify the colour white to be highlighted.)
However using the CreateBitmap method seemed to solve the problem of creating the bitmap however its still working a little strangely.
i now create the bitmap like so
HBITMAP hbmMono = ::CreateBitmap(WorkBmp.bmW idth, WorkBmp.bmHeight, 1, 1, pBmiSrc);
where
int paddedSize = WorkBmp.bmHeight * ( WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4)));
BYTE* pBmiSrc = new BYTE[ paddedSize ];
and send this to the method included below. however it seems to only be pulling back half of the byte array?
instead of outputting
424D62000000000000003E0000 0028000000 0500000009 0000000100 0100000000 0024000000 0000000000 0000000000 0000000000 0000000000 FFFFFF0088 0000007000 0000700000 0070000000 7000000070 0000007000 0000700000 0088000000
i'm outputting
424D62000000000000003E0000 0028000000 0500000009 0000000100 0100000000 0024000000 0000000000 0000000000 0000000000 0000000000 FFFFFF0088 0000007000 0000700000 0070000000 7000CDCDCD CDCDCDCDCD CDCDCDCDCD CDCDCDCDCD
any ideas why this could be?
many thanks for all the help,
Matt.
The difference between the link and what i want to do is that the link will simply convert the image to monochrome and i have no control upon which pixel/s will be black and which will be white (for example in theory i woulld like to be able to specify the colour white to be highlighted.)
However using the CreateBitmap method seemed to solve the problem of creating the bitmap however its still working a little strangely.
i now create the bitmap like so
HBITMAP hbmMono = ::CreateBitmap(WorkBmp.bmW
where
int paddedSize = WorkBmp.bmHeight * ( WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4)));
BYTE* pBmiSrc = new BYTE[ paddedSize ];
and send this to the method included below. however it seems to only be pulling back half of the byte array?
instead of outputting
424D62000000000000003E0000
i'm outputting
424D62000000000000003E0000
any ideas why this could be?
many thanks for all the help,
Matt.
bool CBitmapOCR::WriteToFile(HBITMAP bmp, LPTSTR path)
{
BITMAP WorkBmp;
if(GetObject(bmp, sizeof(WorkBmp), &WorkBmp) == 0)
{
CString dwErr = ::GetLastError();
logMsg(dwErr); //simply prints to a log file in error
}
int paddedSize = WorkBmp.bmHeight * ( WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4)));
BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER *) malloc(sizeof(BITMAPFILEHEADER));
bfh->bfType='B'+('M'<<8);
bfh->bfOffBits= sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + 8; //offset to bitmap pixel data + 8 is for the two colour bytes
bfh->bfSize= paddedSize + bfh->bfOffBits;
bfh->bfReserved1=0;
bfh->bfReserved2=0;
//next the bitmap info
BITMAPINFOHEADER* Bmi = (BITMAPINFOHEADER *) malloc(sizeof(BITMAPINFOHEADER));
Bmi->biSize = sizeof(BITMAPINFOHEADER);
Bmi->biWidth = WorkBmp.bmWidth;
Bmi->biHeight = WorkBmp.bmHeight;
Bmi->biPlanes = 1;//WorkBmp.bmPlanes;
Bmi->biBitCount = 1;//WorkBmp.bmBitsPixel;
Bmi->biCompression = BI_RGB; //BI_RGB=0
Bmi->biSizeImage = paddedSize; //WorkBmp.bmHeight * WorkBmp.bmWidth /8; //should ust be ths as only one bit er pixel
Bmi->biXPelsPerMeter = 0;
Bmi->biYPelsPerMeter = 0;
Bmi->biClrUsed = 0;
Bmi->biClrImportant = 0;
RGBQUAD colors[2];
colors[0].rgbBlue = colors[0].rgbGreen = colors[0].rgbRed = 0;
colors[1].rgbBlue = colors[1].rgbGreen = colors[1].rgbRed = 255;
colors[0].rgbReserved = colors[1].rgbReserved = 0;
BYTE* pBits = new BYTE[ paddedSize ];
GetBitmapBits(bmp,WorkBmp.bmWidth * WorkBmp.bmHeight,pBits);
FILE *p = fopen( path, "wb" );
fwrite( bfh, sizeof(BITMAPFILEHEADER), 1, p );
fwrite( Bmi, sizeof(BITMAPINFOHEADER), 1, p );
fwrite( &colors[0],4, 1, p );
fwrite( &colors[1],4, 1, p );
fwrite( pBits,Bmi->biSizeImage, 1, p );
fclose( p );
return true;
}
hi matt,
the calculation of the padded rowsize does not work properly. if you put a width of 160 the result should be 20, but your formula gives 24
WorkBmp.bmWidth: 160
(WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4))); -> result 24 but should be 20
ike
the calculation of the padded rowsize does not work properly. if you put a width of 160 the result should be 20, but your formula gives 24
WorkBmp.bmWidth: 160
(WorkBmp.bmWidth/8 + (4- (WorkBmp.bmWidth/8%4))); -> result 24 but should be 20
ike
>>>> (4- (WorkBmp.bmWidth/8%4)));
You should add some more parantheses. I don't know in which order the operators / and % were evaluated. It maybe done in that way you expect it ... or not.
>>>> GetBitmapBits(bmp,WorkBmp. bmWidth * WorkBmp.bmHeight,pBits);
The GetBitmapBits will copy the bits of the colored bitmap to a buffer (pBits) which was sized for a monochrome bitmap. Note, when changing the Bmi struct members you do not change any of the 'internal' resources associated to the 'bmp' handle. But that handle (and width and height) was the only info passed to the GetBitmapBits.
You should size the pBits buffer to bmWidth * bmHeight * sizeof(RGBQUAD) and get the colors for the colored bitmap. Then, you could evaluate the RGBQUAD items, and set the bits of a second buffer for the monochrome bitmap (sized to 'paddedSize') like the following:
for (int i = 0; i < WorkBmp.bmWidth * WorkBmp.bmHeight; ++i)
{
RGBQUAD rgbq = *(((RGBQUAD*)pBits) + i); // get ith item of RGBQUAD array
// check color whether it should be black or white
bool black = (rgbq.rgbRed > 178 && ... );
if (black)
{
pMonoBits[i/8] |= 1<<(i%8); // set corresponding bit
}
}
After that you could store the pMonoBits together with the bitmap header you already filled correctly.
Regards, Alex
You should add some more parantheses. I don't know in which order the operators / and % were evaluated. It maybe done in that way you expect it ... or not.
>>>> GetBitmapBits(bmp,WorkBmp.
The GetBitmapBits will copy the bits of the colored bitmap to a buffer (pBits) which was sized for a monochrome bitmap. Note, when changing the Bmi struct members you do not change any of the 'internal' resources associated to the 'bmp' handle. But that handle (and width and height) was the only info passed to the GetBitmapBits.
You should size the pBits buffer to bmWidth * bmHeight * sizeof(RGBQUAD) and get the colors for the colored bitmap. Then, you could evaluate the RGBQUAD items, and set the bits of a second buffer for the monochrome bitmap (sized to 'paddedSize') like the following:
for (int i = 0; i < WorkBmp.bmWidth * WorkBmp.bmHeight; ++i)
{
RGBQUAD rgbq = *(((RGBQUAD*)pBits) + i); // get ith item of RGBQUAD array
// check color whether it should be black or white
bool black = (rgbq.rgbRed > 178 && ... );
if (black)
{
pMonoBits[i/8] |= 1<<(i%8); // set corresponding bit
}
}
After that you could store the pMonoBits together with the bitmap header you already filled correctly.
Regards, Alex
ASKER
Hi itsme and ikework
ikework, thanks for pointing that out i've replaced this to the following now;
double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
int dActualWidth = (int) ((WorkBmp.bmWidth * bytesPerPixel) + 0.999f); //round up to narest byte
while(dActualWidth%4 != 0)
dActualWidth++;
itsme, thanks for that. yes this is the approach trying to take however the problem i mentioned prviously is still happening. (I have noticed though that when i tested i on a 19 x 9 image it worked ok)
so i have my highlighting method working now the problem seems to be i'm not getting enough bitmap bits when trying to write to the file (i printing out the contents of the byte array before creatring the bitmap in the highlight method and this is correct.)
i've changed my GetBitmapBit cal to be
int paddedSize = WorkBmp.bmHeight * dActualWidth;
BYTE* pBits = new BYTE[paddedSize];
GetBitmapBits(bmp, paddedSize, pBits);
i create the HBITMAP bmp as follows
HBITMAP hbmMono = ::CreateBitmap((WORD)WorkB mp.bmWidth , (WORD)WorkBmp.bmHeight, 1, 1, pBmiSrc); //pBmiSrc is correct as i have printed the contents out
am i reading the incorrect amount of bytes?
ikework, thanks for pointing that out i've replaced this to the following now;
double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
int dActualWidth = (int) ((WorkBmp.bmWidth * bytesPerPixel) + 0.999f); //round up to narest byte
while(dActualWidth%4 != 0)
dActualWidth++;
itsme, thanks for that. yes this is the approach trying to take however the problem i mentioned prviously is still happening. (I have noticed though that when i tested i on a 19 x 9 image it worked ok)
so i have my highlighting method working now the problem seems to be i'm not getting enough bitmap bits when trying to write to the file (i printing out the contents of the byte array before creatring the bitmap in the highlight method and this is correct.)
i've changed my GetBitmapBit cal to be
int paddedSize = WorkBmp.bmHeight * dActualWidth;
BYTE* pBits = new BYTE[paddedSize];
GetBitmapBits(bmp, paddedSize, pBits);
i create the HBITMAP bmp as follows
HBITMAP hbmMono = ::CreateBitmap((WORD)WorkB
am i reading the incorrect amount of bytes?
ASKER
hi guys,
i have noticed something strange, as an experiment, due to the fact it seemed to only be sending half the row, i tried the following
HBITMAP hbmMono = ::CreateBitmap((WORD)WorkB mp.bmWidth , (WORD)WorkBmp.bmHeight*2, 1, 1, pBmiSrc);
then when i was writing to the file i did the following
int paddedSize = WorkBmp.bmHeight/2 * dActualWidth;
set the BITMAPHEADERINFO bmHeight to be divided by 2 also. and this is working? why i dont know. any ideas?
i have noticed something strange, as an experiment, due to the fact it seemed to only be sending half the row, i tried the following
HBITMAP hbmMono = ::CreateBitmap((WORD)WorkB
then when i was writing to the file i did the following
int paddedSize = WorkBmp.bmHeight/2 * dActualWidth;
set the BITMAPHEADERINFO bmHeight to be divided by 2 also. and this is working? why i dont know. any ideas?
>>>> when i tested i on a 19 x 9 image
Do you really need to have bitmaps with 'odd' width and height. If your sizes were a multiple of 8 (what is not much for a paper device), you can spare using doubles for calculating bits.
>>>> double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
the following is equivalent:
int bytesPerPixel = (WorkBmp.bmBitsPixel + 7)/8;
It rounds up to the next multiple of 8.
>>>> int paddedSize = WorkBmp.bmHeight * dActualWidth;
That makes paddedSize the number of bytes in case the dActualWidth was calulated correctly. But I have doubts ...
>>>> double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
>>>> int dActualWidth = (int) ((WorkBmp.bmWidth * bytesPerPixel) + 0.999f); //round up to narest byte
>>>> while(dActualWidth%4 != 0)
>>>> dActualWidth++;
That is bad code.
Take 'monochrome' bitmap where bmWidth = 19 and bytesPerPixel is 0.125. Then dActualWidth = 2 and will be padded up to 4.
Take RGB bitmap, where bmWidth = 19 and bytesPerPixel is 4. Then dActualWidth = 76 and was not padded up.
I don't know whether the results were that you expected but the following would give the same results.
int iActualWidth = ((WorkBmp.bmBitsPixel * WorkBmp.bmWidth) + 7) / 8;
iActualWidth += 4 - (iActualWidth%4);
>>>> am i reading the incorrect amount of bytes?
I have problems to see what is the source bitmap and what is the target bitmap. I assumed the passed bmp handle was the handle to a colored bitmap. If so, the GetBitmapBits will return (width * height) * bitsPerPixel bits, which may be padded up to a multiple of 32 bits. Until here there is *not* the sligthest reason to mixup bits and bytes. If you rounded up to a multiple of 32 (once!), you easily can divide by 8 (no roundings) and get the number of bytes the buffer needs to be allocated (and what was returned by GetBitmapBits hopefully). The only variable which may make trouble in the above calculation is the bitsPerPixel as I can't say from your code that you 'retrieve' it somewhere. You would get the *correct* bitsPerPixel for a valid HBITMAP handle if you would call the GetDiBits function with a NULL argument for the lpBits. Then, GetDiBits only fills the BITMAPINFOHEADER structure where the biBitCount member gives the requested information. In your code I can't see how you retrieved the WorkBmp.bmBitsPixel (I saw that you set bmi.biBitCount fix to 1) but that is the essential part.
Can you give some samples where you tell waht you got for 'bmBitsPixel', width and height, paddedSize, and where you tell why you think, that the requested buffer doesn't get all bits. Note, you easily could check that by not creating a monochrome bitmap but creating a copy of the existing bitmap. If you don't get the correct number of bits, that copy would be incomplete. If that works ok, the issue is when you finally 'store' the bits of the monochrome bitmap (where I actually didn't see any code til now).
Do you really need to have bitmaps with 'odd' width and height. If your sizes were a multiple of 8 (what is not much for a paper device), you can spare using doubles for calculating bits.
>>>> double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
the following is equivalent:
int bytesPerPixel = (WorkBmp.bmBitsPixel + 7)/8;
It rounds up to the next multiple of 8.
>>>> int paddedSize = WorkBmp.bmHeight * dActualWidth;
That makes paddedSize the number of bytes in case the dActualWidth was calulated correctly. But I have doubts ...
>>>> double bytesPerPixel = WorkBmp.bmBitsPixel/8.000;
>>>> int dActualWidth = (int) ((WorkBmp.bmWidth * bytesPerPixel) + 0.999f); //round up to narest byte
>>>> while(dActualWidth%4 != 0)
>>>> dActualWidth++;
That is bad code.
Take 'monochrome' bitmap where bmWidth = 19 and bytesPerPixel is 0.125. Then dActualWidth = 2 and will be padded up to 4.
Take RGB bitmap, where bmWidth = 19 and bytesPerPixel is 4. Then dActualWidth = 76 and was not padded up.
I don't know whether the results were that you expected but the following would give the same results.
int iActualWidth = ((WorkBmp.bmBitsPixel * WorkBmp.bmWidth) + 7) / 8;
iActualWidth += 4 - (iActualWidth%4);
>>>> am i reading the incorrect amount of bytes?
I have problems to see what is the source bitmap and what is the target bitmap. I assumed the passed bmp handle was the handle to a colored bitmap. If so, the GetBitmapBits will return (width * height) * bitsPerPixel bits, which may be padded up to a multiple of 32 bits. Until here there is *not* the sligthest reason to mixup bits and bytes. If you rounded up to a multiple of 32 (once!), you easily can divide by 8 (no roundings) and get the number of bytes the buffer needs to be allocated (and what was returned by GetBitmapBits hopefully). The only variable which may make trouble in the above calculation is the bitsPerPixel as I can't say from your code that you 'retrieve' it somewhere. You would get the *correct* bitsPerPixel for a valid HBITMAP handle if you would call the GetDiBits function with a NULL argument for the lpBits. Then, GetDiBits only fills the BITMAPINFOHEADER structure where the biBitCount member gives the requested information. In your code I can't see how you retrieved the WorkBmp.bmBitsPixel (I saw that you set bmi.biBitCount fix to 1) but that is the essential part.
Can you give some samples where you tell waht you got for 'bmBitsPixel', width and height, paddedSize, and where you tell why you think, that the requested buffer doesn't get all bits. Note, you easily could check that by not creating a monochrome bitmap but creating a copy of the existing bitmap. If you don't get the correct number of bits, that copy would be incomplete. If that works ok, the issue is when you finally 'store' the bits of the monochrome bitmap (where I actually didn't see any code til now).
>>>> iActualWidth += 4 - (iActualWidth%4);
Correction:
iActualWidth = (( iActualWidth + 3)/4) * 4;
Correction:
iActualWidth = (( iActualWidth + 3)/4) * 4;
ASKER
hi its me thanks for your reply.
>>Do you really need to have bitmaps with 'odd' width and height. If your sizes were a multiple of 8 (what is not much for a paper device), you can spare using doubles for calculating bits.
potentially the bitmap could be any size so i will need to use doubles
>> int bytesPerPixel = (WorkBmp.bmBitsPixel + 7)/8;
great thank for that.
>>That is bad code.
ok. i see. Just to check then because the reason i moved away from this way was due the tohe poiint the ikework made.
take iActualWidth = 4 (where the actual width is of modulo or 160 as in ikeworks example)
iActualWidth += 4 - (iActualWidth%4);
= 8
will i need to pad like this?
>>I have problems to see what is the source bitmap and what is the target bitmap.
sorry. basically i want to have a method HBITMAP highlightimage(HBITMAP, int,int,int) which will take a sent HBITMAP create a new highlighted monochrome image and then return it. so.. the passed HBITMAP will be the colour bitmap, and the returned HBITMAP will be the monochrome, highlighted one.
I then have another method BOOL WriteToFile(HBITMAP, path) which will write the sent HBITMAP to the specified path.
>>Can you give some samples where you tell waht you got for 'bmBitsPixel', width and height, paddedSize, and where you tell why you think, that the requested buffer doesn't get all bits.
ok, i'll do some test and post the results.
thanks again for all your help.
>>Do you really need to have bitmaps with 'odd' width and height. If your sizes were a multiple of 8 (what is not much for a paper device), you can spare using doubles for calculating bits.
potentially the bitmap could be any size so i will need to use doubles
>> int bytesPerPixel = (WorkBmp.bmBitsPixel + 7)/8;
great thank for that.
>>That is bad code.
ok. i see. Just to check then because the reason i moved away from this way was due the tohe poiint the ikework made.
take iActualWidth = 4 (where the actual width is of modulo or 160 as in ikeworks example)
iActualWidth += 4 - (iActualWidth%4);
= 8
will i need to pad like this?
>>I have problems to see what is the source bitmap and what is the target bitmap.
sorry. basically i want to have a method HBITMAP highlightimage(HBITMAP, int,int,int) which will take a sent HBITMAP create a new highlighted monochrome image and then return it. so.. the passed HBITMAP will be the colour bitmap, and the returned HBITMAP will be the monochrome, highlighted one.
I then have another method BOOL WriteToFile(HBITMAP, path) which will write the sent HBITMAP to the specified path.
>>Can you give some samples where you tell waht you got for 'bmBitsPixel', width and height, paddedSize, and where you tell why you think, that the requested buffer doesn't get all bits.
ok, i'll do some test and post the results.
thanks again for all your help.
>>>> iActualWidth += 4 - (iActualWidth%4);
That was wrong. Look at my correction above.
That was wrong. Look at my correction above.
ASKER
hi its me sorry, maybe i'm reading it wrong but would this work?
>>>iActualWidth = (( iActualWidth + 3)/4) * 4;
e.g. for 4
4+3 = 7
and the division and multplying would cancel each other out?
>>>iActualWidth = (( iActualWidth + 3)/4) * 4;
e.g. for 4
4+3 = 7
and the division and multplying would cancel each other out?
>>>> and the division and multplying would cancel each other out?
No, it is an integer divison which would round to the next lower integer, so
((0 + 3)/4) * 4 = 0 * 4 = 0
((1 + 3)/4) * 4 = 1 * 4 = 4
((2 + 3)/4) * 4 = 1 * 4 = 4
((3 + 3)/4) * 4 = 1 * 4 = 4
((4 + 3)/4) * 4 = 1 * 4 = 4
((5 + 3)/4) * 4 = 2 * 4 = 8
((6 + 3)/4) * 4 = 2 * 4 = 8
((7 + 3)/4) * 4 = 2 * 4 = 8
((8 + 3)/4) * 4 = 2 * 4 = 8
((9 + 3)/4) * 4 = 3 * 4 = 12
the whole thing always rounds up to the next multiple of 4.
No, it is an integer divison which would round to the next lower integer, so
((0 + 3)/4) * 4 = 0 * 4 = 0
((1 + 3)/4) * 4 = 1 * 4 = 4
((2 + 3)/4) * 4 = 1 * 4 = 4
((3 + 3)/4) * 4 = 1 * 4 = 4
((4 + 3)/4) * 4 = 1 * 4 = 4
((5 + 3)/4) * 4 = 2 * 4 = 8
((6 + 3)/4) * 4 = 2 * 4 = 8
((7 + 3)/4) * 4 = 2 * 4 = 8
((8 + 3)/4) * 4 = 2 * 4 = 8
((9 + 3)/4) * 4 = 3 * 4 = 12
the whole thing always rounds up to the next multiple of 4.
As told above you better should calculate the bits, round up to a multiple of 32 and get the bytes then.
Microsoft samples do that by using the following macro:
// How wide, in bytes, would this many bits be, DWORD aligned?
#define WIDTHBYTES(bits) ((((bits) + 31)>>5)<<2)
The above takes the number of bits, adds 31, divides by 32 (a right shift of 5 is the same as dividing by 2^5==32) and multiplies the result by 4 (a left-shift of 2 is the same as multiplying with 2^2 == 4).
Microsoft samples do that by using the following macro:
// How wide, in bytes, would this many bits be, DWORD aligned?
#define WIDTHBYTES(bits) ((((bits) + 31)>>5)<<2)
The above takes the number of bits, adds 31, divides by 32 (a right shift of 5 is the same as dividing by 2^5==32) and multiplies the result by 4 (a left-shift of 2 is the same as multiplying with 2^2 == 4).
ASKER
sorry about that itsme, didnt see the integer division. i've been testing the method on 24bit bitmaps and (using paint) and it doesnt work for any bitmap i pass it. for example in the following bitmap
424D9A00000000000000360000 0028000000 0600000005 0000000100 1800000000 0064000000 0000000000 0000000000 0000000000 0000000000 0000000000 00000000FF 0000FF0000 00FFFFFF00 0000000000 00000000FF 0000FF0000 00FFFFFFFF FFFF000000 00000000FF 0000FF0000 00FFFFFFFF FFFFFFFFFF 00000000FF 0000FF0000 00FFFFFFFF FFFFFFFFFF FFFFFF00FF 0000FF0000 00
i get the following output from iterating through the byte array
ffffffffffffffffffffffff00 ff0000ff00 ffffffffff ffffffff00 000000ff00 00ff00ffff ffffffff00 0000000000 00ff0000ff 00ffffff00 0000000000 00000000ff 0000ff0000 0000000000 0000000000 0000ff0000 ff00000000 0000000000 0000
i'll do some more tests on different bitmaps, but any ideas here?
424D9A00000000000000360000
i get the following output from iterating through the byte array
ffffffffffffffffffffffff00
i'll do some more tests on different bitmaps, but any ideas here?
HBITMAP CBitmapOCR::HighlightImage(HBITMAP hBmp, int rVal, int gVal, int bVal)
{
BITMAP WorkBmp;
if(GetObject(hBmp, sizeof(WorkBmp), &WorkBmp) == 0)
{
CString dwErr = ::GetLastError();
logMsg(dwErr);
}
//pad so multiple of 4
int bytesPerPixel = ((WorkBmp.bmBitsPixel+7)/8);
int dActualWidth = (( (WorkBmp.bmWidth * bytesPerPixel) + 3)/4) * 4;
//calculate the mono bitmap width
int dActualMonoWidth = (( (WorkBmp.bmWidth * 0.125) + 3)/4) * 4;
int paddedSize = WorkBmp.bmHeight * dActualMonoWidth;
BYTE* pBmiSrc = new BYTE[ paddedSize ];
BYTE* bmBits = (BYTE*)GlobalAlloc(GPTR,WorkBmp.bmWidthBytes*WorkBmp.bmHeight);
GetBitmapBits(hBmp,dActualWidth*WorkBmp.bmHeight,bmBits);//was bmwidthbytes
int x;
for(x=0; x<dActualWidth*WorkBmp.bmHeight;x++)
{ //simply logs out to file
ocrMsg.Format(":Original Byte:%x: count[%d]", bmBits[x],x);
logMsg(ocrMsg);
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
hi itsme, thanks for the reply and your help you've been brilliant.
ok i will be wanting to process both 24 and 32 bit bitmap so i will have to make the method general rather than just for 32bit.
>> (allBitsOfBitmap + 31) / 32
so to check, would to following work generically to calculate the actualwidht of a bitmap line?
(((width*bitsperpixel)+31) /32) * 4; // i.e. using th WIDTHBYTES macro?
so for a 5x5 bitmap for example
for 24bit;
(((5 * 24) + 31) /32) * 4 = 120 + 31 / 32 *4 = 151 /32 *4 = 4 * 4 = 16
for 32 bit
(((5 * 32) + 31) /32) * 4 = 160 + 31 / 32 *4 = 191 /32 *4 = 5 * 4 = 20
ok i will be wanting to process both 24 and 32 bit bitmap so i will have to make the method general rather than just for 32bit.
>> (allBitsOfBitmap + 31) / 32
so to check, would to following work generically to calculate the actualwidht of a bitmap line?
(((width*bitsperpixel)+31)
so for a 5x5 bitmap for example
for 24bit;
(((5 * 24) + 31) /32) * 4 = 120 + 31 / 32 *4 = 151 /32 *4 = 4 * 4 = 16
for 32 bit
(((5 * 32) + 31) /32) * 4 = 160 + 31 / 32 *4 = 191 /32 *4 = 5 * 4 = 20
>>>> for 24bit;
>>>> (((5 * 24) + 31) /32) * 4 = 120 + 31 / 32 *4 = 151 /32 *4 = 4 * 4 = 16
>>>> for 32 bit
>>>> (((5 * 32) + 31) /32) * 4 = 160 + 31 / 32 *4 = 191 /32 *4 = 5 * 4 = 20
Yes, for 24 bits you have 3 bytes per pixel, so it is 15 bytes padded up to the next multiple of 4 --> 16
Yes, for 32 bits you have 4 bytes per pixel, so it is 20 bytes what is already a multiple of 4 --> 20
>>>> (((5 * 24) + 31) /32) * 4 = 120 + 31 / 32 *4 = 151 /32 *4 = 4 * 4 = 16
>>>> for 32 bit
>>>> (((5 * 32) + 31) /32) * 4 = 160 + 31 / 32 *4 = 191 /32 *4 = 5 * 4 = 20
Yes, for 24 bits you have 3 bytes per pixel, so it is 15 bytes padded up to the next multiple of 4 --> 16
Yes, for 32 bits you have 4 bytes per pixel, so it is 20 bytes what is already a multiple of 4 --> 20
ASKER
great thanks itsme, ok so now to access each individual pixel in the byte array i will need to do the following? . e.g. to just prin out each pixels rgb value
int bytesPerPixel = ((WorkBmp.bmBitsPixel+7)/8 ); //will give 4 for 32bit and 3 for 24bit
int dActualWidth = (((WorkBmp.bmWidth*WorkBmp .bmBitsPix el)+31)/32 ) * 4;
for(y=0;y<WorkBmp.bmHeight ;y++)
{
for(x=0;x<WorkBmp.bmWidth; x++)
{
r = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 2);
g = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 1);
b = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 0);
ocrMsg.Format("TEST [x%d,y%d] r%d,g%d,b%d",x,y,r,g,b); //basically just outputs text to log file
logMsg(ocrMsg);
}
}
int bytesPerPixel = ((WorkBmp.bmBitsPixel+7)/8
int dActualWidth = (((WorkBmp.bmWidth*WorkBmp
for(y=0;y<WorkBmp.bmHeight
{
for(x=0;x<WorkBmp.bmWidth;
{
r = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 2);
g = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 1);
b = *((BYTE*)WorkBmp.bmBits + (y * dActualWidth) + (x * bytesPerPixel) + 0);
ocrMsg.Format("TEST [x%d,y%d] r%d,g%d,b%d",x,y,r,g,b); //basically just outputs text to log file
logMsg(ocrMsg);
}
}
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
thanks for that itsme,
it working perfectly. ;)
it working perfectly. ;)
ASKER
Itsme broke the question down with detailed examples, helping to understand the process clearly and easiely. many thanks
maybe the problem comes from how you allocate the BITMAPINFO:
> LPBITMAPINFO bi = (LPBITMAPINFO) malloc(sizeof(LPBITMAPINFO
First you use 'sizeof(LPBITMAPINFO)' which is for sure wrong since this just allocates the size of a pointer (4 bytes) ... further I think you'll have to allocate in addition the memory for the colortable, so maybe somehow like this should work:
> LPBITMAPINFO bi = (LPBITMAPINFO) malloc(sizeof( BITMAPINFO ) + 2 * sizeof( RGBQUAD ) );
Hope that helps,
ZOPPO