BitBlt绘制位图上下颠倒

4
我有一个MFC控件,其中我传递了一个位图的句柄(HBITMAP)。在控件的OnPaint方法中,我使用BitBlt来渲染位图。但是位图被倒置渲染。
作为测试,我从这个句柄创建了一个CBitmap对象,并将其写入文件,它创建了一个正确的位图。那么我的BitBlt调用是否有问题?
下面是我的OnPaint代码。我尝试将设备上下文的映射模式更改为MM_LOENGLISH,并且能够使位图正常渲染,但它很粗糙。当我将映射模式保留为MM_TEXT时,图像质量完美,但正如我所说,它是倒置的。我没有与位图、Blitting等工作过,因此可能会漏掉一些简单的东西。欢迎提出其他建议。背景是我正在从视频相机驱动程序中获取BYTE*并创建HBITMAP以渲染视频。
如何使其正确渲染?谢谢。
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);
}

编辑* 我曾认为,我可以将位图以正确的方向保存到磁盘中,因此问题出在bitblt上。以下是我生成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;
}

哦,我使用临界区是因为我将hbitmap传递给控件中的属性,然后在OnPaint中访问它。如果这可能会有问题,我将不得不重新考虑。谢谢


可能与您创建 _handleBMP 的方式有关,而不是您绘制它的方式有关。 - Adrian McCarthy
2
这几乎可以确定是在创建位图的代码中出错了。请注意,扫描行是倒置存储的。顺便说一下,在绘制代码中使用EnterCriticalSection是一个明显的红旗,因为GDI基本上不是线程安全的。 - Hans Passant
4个回答

8
只需在 BITMAPINFOHEADER 结构的 biHeight 字段中使用负值即可。
bi.biHeight = -height;  //this is the line that makes it draw upside down or not

8

Windows位图是从底部开始储存。世界上大部分其他地方都是从顶部开始工作,所以我猜这就是你从相机中得到的东西。

您可以在BITMAPINFOHEADER结构中使用负高度来反转正常顺序。


我将BITMAPINFOHEADER对象中的biHeight属性从480更改为-480,但没有任何效果,它仍然是倒置渲染的。 - mash
@mash,希望你能明白我想在位图由原始数据创建的地方进行更改。如果我没有表达清楚,抱歉。 - Mark Ransom
是的,我认为我设置得太晚了。我从DirectShow图形中获取原始数据。我的图形由源过滤器和样本抓取器组成,我的样本抓取器将原始数据发布给订阅者之一,其中之一就是我的渲染器。我想我需要以某种方式为我的源过滤器设置属性?我还不确定如何做到这一点。 - mash

1

好的,看起来我已经解决了这个问题。我最终将映射模式更改为MM_LOENGLISH。正如我之前所说,这给了我一个粗糙的图像,但是通过添加以下内容进行了纠正。

dc.SetStretchBltMode(COLORONCOLOR);

我需要先读一些资料才能弄清楚为什么...但这是我的渲染代码。

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

1
MM_TEXT中,y轴向下,而在其他映射模式中,它向上。尝试使用MM_ISOTROPIC映射模式。为了更精确的控制,您可能需要在CDC上设置视口和窗口偏移和范围。
位图可以倒置存储,这在BITMAPINFOHEADER结构中通过负高度表示,但这不应该是问题。

你可以直接取反结构体中的高度,强制它在不修改映射模式的情况下绘制另一侧。 - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接