Problem with line alignment for Bitmap data
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 6 of 6

Thread: Problem with line alignment for Bitmap data

Hybrid View

  1. #1
    Join Date
    Apr 2011
    Posts
    20

    Question 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
    Attached Images Attached Images   
    Last edited by vanselm; July 28th, 2011 at 11:01 AM.

  2. #2
    Join Date
    Apr 2009
    Posts
    598

    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);

  3. #3
    Join Date
    Apr 2011
    Posts
    20

    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

  4. #4
    Join Date
    Apr 2011
    Posts
    20

    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);

  5. #5
    Join Date
    Jul 2011
    Posts
    4

    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/ .

  6. #6
    Join Date
    Jul 2011
    Posts
    3

    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
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center