4
votes

I have an MFC control to which I pass a handle to a bitmap (HBITMAP). In the controls OnPaint method I am using BitBlt to render the bitmap. But the bitmap is being rendered upside down.

As a test I created a CBitmap object from this handle and write that out to a file and it created a bitmap that was right side up. So am I doing something wrong with my call to BitBlt?

I've posted my code from OnPaint below. I did try to change the mapping mode of my device context to MM_LOENGLISH and was able to get the bitmap to render right side up but it was very grainy. When I leave the mapping mode at MM_TEXT the quality of the image is perfect but as I said it is upside down. I haven't worked much with bitmaps, blitting etc...so I could be missing something easy. Any other suggestions would be appreciated. For some background I'm grabbing a BYTE* from a video camera driver and creating the HBITMAP to render the video. How can I get this to render properly?? Many thanks

void BitmapControl::OnPaint()
{
EnterCriticalSection (&CriticalSection);

if (_handleBMP)
{

    CPaintDC dc(this);
    //dc.SetMapMode(MM_LOENGLISH);
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);

    CRect rect;
    GetClientRect(&rect);
    dc.DPtoLP(&rect);


    CBitmap* pBmpOld = dcMem.SelectObject(CBitmap::FromHandle(_handleBMP));
    BitBlt(dc,rect.left,rect.top,rect.Width(),rect.Height(),dcMem,rect.left,rect.top,SRCCOPY); //works with MM_TEXT but upsidedown
    //BitBlt(dc,0,rect.bottom,rect.Width(),-rect.Height(),dcMem,0,0,SRCCOPY); //works with MM_LOENGLISH
    dcMem.SelectObject(pBmpOld);
    DeleteDC(dc);
    DeleteDC(dcMem);
    DeleteObject(_handleBMP);
    DeleteObject(pBmpOld);
    _handleBMP = NULL;

}
LeaveCriticalSection (&CriticalSection);
}

edit* I was assuming because I could save the bitmap to disk in the correct orientation that the problem was with bitblt. Here is the code I use to generate the HBITMAP.

 HBITMAP BitmapWriter::CreateBitmapFromFrame(BYTE* frame)
{
BITMAPFILEHEADER* bmfh;
bmfh = (BITMAPFILEHEADER*)frame;

BITMAPINFOHEADER* bmih = &_bmi;

BITMAPINFO* bmpInfo = (BITMAPINFO*)bmih;

HBITMAP hbmp = CreateDIBSection(_hdc,bmpInfo,DIB_RGB_COLORS,NULL,NULL,0);
SetBitmapBits(hbmp,_bmi.biSizeImage,frame);


return hbmp;
}

Oh, and I used the critical section because I pass the hbitmap to the control in a property and then access it in the OnPaint. If that's a potential problem I will have to rethink that. Thanks

4
It possibly has more to do with how you created _handleBMP than with the way you're drawing it.Adrian McCarthy
This almost certainly started wrong in the code that created the bitmap. Beware that the scan lines are stored upside-down. Btw, EnterCriticalSection is a raw red flag in painting code, GDI is fundamentally not thread-safe.Hans Passant

4 Answers

7
votes

Windows bitmaps are stored with the bottom line first. Most of the rest of the world works top line first, so I presume that's what you're getting from your camera.

You can use a negative height in the BITMAPINFOHEADER structure to reverse the normal order.

7
votes

Just use a Negative value in the biHeight field of BITMAPINFOHEADER struct.

bi.biHeight = -height;  //this is the line that makes it draw upside down or not
1
votes

In MM_TEXT y-axis points down, while in the other mapping modes it points up. Try MM_ISOTROPIC mapping mode. For more precise control, you may need to set viewport and window offset and extents on CDC.

Bitmaps can be stored upside down, that is indicated by negative height in the BITMAPINFOHEADER structure, but this should not be a problem.

1
votes

Ok, I seem to have this working. I ended up changing the mapping mode to MM_LOENGLISH. As I said before this gave me a grainy image but this was corrected by adding the following

dc.SetStretchBltMode(COLORONCOLOR);

I need to do a bit of reading to actually figure out why...but here is my rendering code now.

void BitmapControl::OnPaint()
{
EnterCriticalSection (&CriticalSection);

if (_handleBMP)
{

    CPaintDC dc(this);
    //dc.SetMapMode(MM_ISOTROPIC);
    dc.SetMapMode(MM_LOENGLISH);
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);

    CRect rect;
    GetClientRect(&rect);
    dc.DPtoLP(&rect);

    CBitmap* pBmpOld = dcMem.SelectObject(CBitmap::FromHandle(_handleBMP));

    //tst
    dc.SetStretchBltMode(COLORONCOLOR);

    //BitBlt(dc,rect.left,-0,rect.Width(),rect.Height(),dcMem,rect.left,rect.top,SRCCOPY); //works with MM_TEXT but upsidedown
    BitBlt(dc,0,rect.bottom,rect.Width(),-rect.Height(),dcMem,0,0,SRCCOPY); //works with MM_LOENGLISH
    dcMem.SelectObject(pBmpOld);
    DeleteDC(dc);
    DeleteDC(dcMem);
    DeleteObject(_handleBMP);
    DeleteObject(pBmpOld);
    _handleBMP = NULL;

}
LeaveCriticalSection (&CriticalSection);
}