-
July 28th, 2011, 10:51 AM
#1
Problem with line alignment for Bitmap data
Hello,
I have already searched on the Internet about how to align each line of the bitmap data, so that it is displayed correctly. The lines must be a multiple of 4 Byte. I also tried to do so, however the problem still exists. As long as the image width is a multiple of 4 Byte, then it is displayed correctly otherwise, the lines are shifted against each other, so that the image is displayed corruptly ( see the attached files).
my steps to display the image are:
1) Load the image from the .bmp file
2) get its bits
3) Create and modify the BITMAPINFO struct, in order to pass it to SetStretchBltMode
4) display the image with "SetStretchBltMode"
Code:
// Enable button 'Load Image'
GetDlgItem(IDC_BUTTON_LOAD_IMAGE)->EnableWindow(FALSE);
CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, _T("BMP Windows Bitmap|*.bmp|PNG Portable Network Graphics|*.png"));
dlg.m_ofn.nFilterIndex = 1;
dlg.m_ofn.lpstrTitle = _T("Search Test Image");
if(dlg.DoModal() == IDOK)
{
m_strFileName = dlg.GetPathName();
UpdateData(FALSE);
HBITMAP hBitMap = (HBITMAP)LoadImage(NULL, m_strFileName,IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
if (NULL != hBitMap)
{
CBitmap bmp;
bmp.Attach((HBITMAP)hBitMap); // handle you got from LoadBitmap
BITMAP bitmap;
bmp.GetBitmap(&bitmap);
int paddedRow = TJPAD(bitmap.bmWidth*bitmap.bmBitsPixel/8);
unsigned long bufferSize = bitmap.bmHeight*paddedRow;
if (NULL != m_bufferBeforeCompression)
{
delete[] m_bufferBeforeCompression;
m_bufferBeforeCompression = NULL;
}
m_bufferBeforeCompression = new unsigned char[bufferSize];
//::GetBitmapBits((HBITMAP)hBitMap,bufferSize,m_bufferBeforeCompression );
bmp.GetBitmapBits(bufferSize,m_bufferBeforeCompression );
// m_bufferBeforeCompression is the pointer to the raw image data
m_nSizeX = bitmap.bmWidth;
m_nSizeY = bitmap.bmHeight;
m_nBitsPerPixel = bitmap.bmBitsPixel;
// if it is a grayscale image, then only the grayscale subsampling for compression is possible
if (m_nBitsPerPixel == 8)
{
m_cbSubsampling.SetCurSel(3);
}
display_frame(m_bufferBeforeCompression);
...
and here is the function which displays the image data:
Code:
void CVITA_MFCDlg::display_frame(unsigned char * ImageData)
{
// get the device handle of the picture control
CDC* pdc = GetDlgItem(IDC_DISPLAY_TEST)->GetDC();
HDC hdc = pdc->m_hDC;
// get the dimensions of the device window
RECT rcClient;
GetDlgItem(IDC_DISPLAY_TEST)->GetClientRect((LPRECT)&rcClient);
INT picCtrlWidth = rcClient.right;
INT picCtrlHeight = rcClient.bottom;
BITMAPINFO* BmInfo;
if (m_nBitsPerPixel == 8)
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
for(int i=0; i<256; i++)
{
BmInfo->bmiColors[i].rgbRed = i;
BmInfo->bmiColors[i].rgbGreen = i;
BmInfo->bmiColors[i].rgbBlue = i;
BmInfo->bmiColors[i].rgbReserved = 0;
}
}
else
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFO));
}
BmInfo->bmiHeader.biSize = sizeof (BmInfo->bmiHeader);
BmInfo->bmiHeader.biWidth = m_nSizeX;
BmInfo->bmiHeader.biHeight = -m_nSizeY;
BmInfo->bmiHeader.biPlanes = 1;
BmInfo->bmiHeader.biBitCount = m_nBitsPerPixel;
BmInfo->bmiHeader.biCompression = BI_RGB;
//BmInfo->bmiHeader.biSizeImage = ((m_nSizeX * m_nBitsPerPixel +31) & ~31) /8 * m_nSizeY;
BmInfo->bmiHeader.biSizeImage = m_nSizeY * TJPAD(m_nSizeX*m_nBitsPerPixel/8);
BmInfo->bmiHeader.biClrUsed = 0;
BmInfo->bmiHeader.biClrImportant = 0;
// use HALFTONE or MAXSTRETCHBLTMODE stretch mode, otherwise there are some artifacts within the image
SetStretchBltMode(hdc,HALFTONE);
// display the content of the array on an output device and stretch the image accordingly to the device window dimensions
::StretchDIBits( hdc, // hDC
0, // DestX
0, // DestY
picCtrlWidth, // nDestWidth
picCtrlHeight, // nDestHeight
0, // SrcX
0, // SrcY
m_nSizeX, // SrcWidth
m_nSizeY, // SrcHeight
(LPVOID)((LPDWORD)ImageData), // lpBits
(LPBITMAPINFO)BmInfo, // lpBitsInfo
DIB_RGB_COLORS, // wUsage
SRCCOPY); // wDwRop
// make the device context free for application
ReleaseDC(pdc);
}
Code:
#define TJPAD(p) (((p)+3)&(~3))
Could you please take a look at my code and tell me what I have to change in order to avoid the strange display bahaviour. Thanks.
best regards
Last edited by vanselm; July 28th, 2011 at 11:01 AM.
-
July 29th, 2011, 04:16 AM
#2
Re: Problem with line alignment for Bitmap data
DWORD-aligned bytes is needed.
Instead of
Code:
#define TJPAD(p) (((p)+3)&(~3))
int paddedRow = TJPAD(bitmap.bmWidth*bitmap.bmBitsPixel/8);
Have
Code:
#define TJPAD(bits) ((((bits) + 31) & ~31) >> 3)
int paddedRow = TJPAD(bitmap.bmWidth*bitmap.bmBitsPixel);
The division is done at the end (with ">>3", which is the same than "/ 8") instead of being done in the middle of the computation. Therefore we need to use 31 instead of 3.
Let's take an example where an image has only one row of 3 pixels, with 2 bits per pixel
It is obvious we need 4 bytes for that picture.
1. With your algortihm:
3 * 2 / 8 = 0
0 + 3 = 3
3 & ~3 = ...0011 & ...1100 = ...0000 = 0
0 byte, sorry, that's not enough!
2. With the new algorithm:
3 * 2 = 6
6 + 31 = 37
37 & ~31 = ...00010111 & ...11100000 = 32
32 >> 3 = 4
4 bytes, It's ok.
You also need to switch the comments on your lines:
Code:
//BmInfo->bmiHeader.biSizeImage = ((m_nSizeX * m_nBitsPerPixel +31) & ~31) /8 * m_nSizeY;
BmInfo->bmiHeader.biSizeImage = m_nSizeY * TJPAD(m_nSizeX*m_nBitsPerPixel/8);
-
July 29th, 2011, 04:42 AM
#3
Re: Problem with line alignment for Bitmap data
Thanks for your reply.
Unfortunately, it does not solve my problem because both padding algorithms are correct. It does not matter, whether I use your suggested algorithm or mine, the results are the same, for the image width of 251 pixels for example. The row stride without padding would be 251*3 = 753 Bytes and with padding 756 bytes.
Code:
imgWidth = 251;
m_nBitsPerPixel = 24;
long first = ((imgWidth * m_nBitsPerPixel +31) & ~31) /8; // returns 756
long second = TJPAD(imgWidth * m_nBitsPerPixel/8); // returns 756
That means the padding calculation is correct anyway. The question is: where should I use the padded row. May be the getting of raw bits from .bmp file is the weak point in my code. I tried to use GetDIBits() instead of using CBitmap Class and its members, but it returns me always an error. I guess some of the passed parameters are not correct.
Code:
BITMAPINFO* BmInfo;
if (m_nBitsPerPixel == 8)
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
for(int i=0; i<256; i++)
{
BmInfo->bmiColors[i].rgbRed = i;
BmInfo->bmiColors[i].rgbGreen = i;
BmInfo->bmiColors[i].rgbBlue = i;
BmInfo->bmiColors[i].rgbReserved = 0;
}
}
else
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFO));
}
BmInfo->bmiHeader.biSize = sizeof (BmInfo->bmiHeader);
BmInfo->bmiHeader.biWidth = m_nSizeX;
BmInfo->bmiHeader.biHeight = -m_nSizeY;
BmInfo->bmiHeader.biPlanes = 1;
BmInfo->bmiHeader.biBitCount = m_nBitsPerPixel;
BmInfo->bmiHeader.biCompression = BI_RGB;
BmInfo->bmiHeader.biSizeImage = ((m_nSizeX * m_nBitsPerPixel +31) & ~31) /8 * m_nSizeY;
BmInfo->bmiHeader.biClrUsed = 0;
BmInfo->bmiHeader.biClrImportant = 0;
HDC hDC = CreateCompatibleDC( NULL );
SelectObject( hDC, hBitMap );
int iRet = GetDIBits( hDC, hBitMap, 0, bitmap.bmHeight, m_bufferBeforeCompression, BmInfo, DIB_RGB_COLORS );
DeleteObject( hBitMap );
Hope you can help me anyway. Thanks
-
July 29th, 2011, 05:59 AM
#4
Re: Problem with line alignment for Bitmap data
Problem solved!
For your interest, the problem was caused by the kind of how getting the image data from file.
I use the GetDIBits() function now and there are no problems for displaying images that have widths not multiple of 4 Byte.
Here is the code:
Code:
HINSTANCE hInstance = AfxGetInstanceHandle();
HANDLE hBitMap = LoadImage(hInstance, m_strFileName,IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
if (NULL != hBitMap)
{
BITMAP bitmap;
//Fill out the BITMAP structure.
int lsuccess = GetObject(hBitMap, sizeof(bitmap), (LPVOID)&bitmap);
HDC hDC = CreateCompatibleDC( NULL );
SelectObject( hDC, hBitMap );
BITMAPINFO* BmInfo;
if (m_nBitsPerPixel == 8)
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
for(int i=0; i<256; i++)
{
BmInfo->bmiColors[i].rgbRed = i;
BmInfo->bmiColors[i].rgbGreen = i;
BmInfo->bmiColors[i].rgbBlue = i;
BmInfo->bmiColors[i].rgbReserved = 0;
}
}
else
{
BmInfo = (BITMAPINFO*)alloca( sizeof(BITMAPINFO));
}
BmInfo->bmiHeader.biSize = sizeof (BmInfo->bmiHeader);
BmInfo->bmiHeader.biWidth = bitmap.bmWidth;
BmInfo->bmiHeader.biHeight = -bitmap.bmHeight;
BmInfo->bmiHeader.biPlanes = 1;
BmInfo->bmiHeader.biBitCount = bitmap.bmBitsPixel;
BmInfo->bmiHeader.biCompression = BI_RGB;
BmInfo->bmiHeader.biSizeImage = bitmap.bmHeight * TJPAD(bitmap.bmWidth*bitmap.bmBitsPixel/8);
// m_bufferBeforeCompression is the pointer to the raw image data
unsigned long bufferSize = bitmap.bmHeight * TJPAD(bitmap.bmWidth*bitmap.bmBitsPixel/8);
if (NULL != m_bufferBeforeCompression)
{
delete[] m_bufferBeforeCompression;
m_bufferBeforeCompression = NULL;
}
m_bufferBeforeCompression = new unsigned char[bufferSize];
// Get the image data from the bitmap
if(0 == GetDIBits(hDC, (HBITMAP)hBitMap, 0, bitmap.bmHeight, m_bufferBeforeCompression, BmInfo, DIB_RGB_COLORS))
{
// error handling
AfxMessageBox(_T("GetDIBits() error"),MB_ICONERROR);
}
m_nSizeX = bitmap.bmWidth;
m_nSizeY = bitmap.bmHeight;
m_nBitsPerPixel = bitmap.bmBitsPixel;
display_frame(m_bufferBeforeCompression);
-
July 29th, 2011, 10:51 AM
#5
Re: Problem with line alignment for Bitmap data
If one line of the bitmap is not aligned, you may re-package them with 1-3 bytes added to each line. However, they may be a straight line with black or white color exists. I'm sure you may find some tips from the related open sources http://www.becoding.com/source/bitmap+alignment/ .
-
July 29th, 2011, 02:51 PM
#6
Re: Problem with line alignment for Bitmap data
Hello...
Thank you for giving us the solution of that problem. It is very useful and important information for those who are new in programming and in this forum like me..
portable photo scanner
Last edited by Arleen; August 2nd, 2011 at 10:48 PM.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|