【问题标题】:Blend 2 bitmaps混合 2 个位图
【发布时间】:2016-01-01 21:08:50
【问题描述】:

我有 2 个缓冲区指向不同大小的 RGB32 图像,所以我的想法是缩放一个缓冲区以匹配另一个缓冲区并对这些图像进行 alphablend。

目前,我能够将 StretchBlt(用于缩放性能)和 GDI+ drawimage 函数与用于 alphablending 的颜色矩阵混合。这似乎有点慢,而且使用 DirectX 的不同组件使用缓冲区也存在问题。对于缓冲区问题,我尝试以相反的顺序复制行,但它在 DirectX 相关组件中除外。

Bitmap bmp1(width, height, 4bytesperpixel, RGB32, bufferpointer1);
Bitmap blend(width, height, 4bytesperpixel);
Graphics g(&newbmp)

using GDI function
Bitmap bmp2(scaleWidth, scaleHeight, 4bytesperpixel, RGB32, bufferpointer2)
HDC memdc = g.GetHDC();

////  scaling the bufferpointer2 to actual width & height
StretchDIBits(memdc, x,y, width, height, 0, 0,scaleWidth, scaleHeight, bufferpointer2,..)
g.ReleaseDC(memdc); // so that content is copied to the bitmap

//// Then alphablending bmp1 on top of the scaled imaged bmp2
//// Using lockbits to copy the bitmap bytes and unlocking it.

所以我需要替换 GDI+ 函数并为此使用像 AlphaBlend 这样的 Win32 函数。我试过这样的东西,它显示一个黑屏

    BITMAPINFO bminfo1 = {};
    bminfo1.bmiHeader.biSize = sizeof( BITMAPINFO );
    bminfo1.bmiHeader.biWidth = w;
    bminfo1.bmiHeader.biHeight = h;
    bminfo1.bmiHeader.biBitCount = m_nBytesPerPixel * 8;
    bminfo1.bmiHeader.biCompression = BI_RGB;
    bminfo1.bmiHeader.biPlanes = 1;

    BITMAPINFO bminfo2 = {};
    bminfo2.bmiHeader.biSize = sizeof( BITMAPINFO );
    bminfo2.bmiHeader.biWidth = sW;
    bminfo2.bmiHeader.biHeight = sH;
    bminfo2.bmiHeader.biBitCount = m_nBytesPerPixel * 8;
    bminfo2.bmiHeader.biCompression = BI_RGB;
    bminfo2.bmiHeader.biPlanes = 1;

    char* pBytes1, *pBytes2;

    HDC hmemdc1 = CreateCompatibleDC(GetDC(0));
    HDC hmemdc2 = CreateCompatibleDC(GetDC(0));

    HBITMAP hBitmap1 = CreateDIBSection(hmemdc1, &bminfo1, DIB_RGB_COLORS, (void**) &pBytes1, NULL, 0);
    SetDIBits(hmemdc1, hBitmap1, 0, bminfo1.bmiHeader.bih, pBuffer[0], &bminfo1, DIB_RGB_COLORS);

    HBITMAP hBitmap2 = CreateDIBSection(hmemdc2, &bminfo2, DIB_RGB_COLORS, (void**) &pBytes2, NULL, 0);
    SelectObject(hmemdc2,hBitmap2);
    StretchDIBits(hmemdc2, 0, 0, w, h, 0, 0,
        sW, sH, pBuffer[1], &bminfo2, DIB_RGB_COLORS, SRCCOPY );

    BLENDFUNCTION bStruct; 
    bStruct.BlendOp = AC_SRC_OVER;
    bStruct.BlendFlags = 0;
    bStruct.SourceConstantAlpha = 255;
    bStruct.AlphaFormat = AC_SRC_ALPHA;

    SelectObject(hmemdc1,hBitmap1);
    SelectObject(hmemdc2,hBitmap2);

    //blend bmp2 on bmp1
    BOOL res = AlphaBlend(hmemdc1, 0, 0, w, h, hmemdc2, 0, 0, w, h, bStruct);

    //for testing output
    SelectObject(hmemdc1,hBitmap1);
    BitBlt(GetDC(0),0,0,width,height,hmemdc1,100,100,SRCCOPY);


    //copy the bitmap buffer
    memcpy(out, pBytes1, (w * m_nBytesPerPixel) * h);

我不确定是否可以使用 AlphaBlend 函数来混合基于 2 个内存 DC 的每个像素的位图。任何帮助将不胜感激。

【问题讨论】:

  • 请出示真实代码。目前无法判断您是否忘记了一些简单的事情(例如,您没有表明您选择 hBitmap1hmemdc1)或者是否还有更多内容。
  • AlphaBlend 肯定会在两个内存位图上工作。我会检查 StretchDIBits 的结果,并且你已经正确设置了 blendfunction 参数
  • @JonathanPotter 我已经编辑以添加完整的代码。 gordy,使用的 StretchDIBits 和 Bitblt 函数按预期工作
  • SetDIBits 用于 DDB,但 hBitmap1 是 DIB - 不确定这是否可行。你可以试试SetDIBitsToDevice。如果您使用AC_SRC_ALPHA,您就是在告诉GDI 源位图有一个(预倍增的)alpha 通道。可以? m_nBytesPerPixel 的值是多少?
  • 其他问题包括BitBlt(GetDC(0), ...,这是一个明显的资源泄漏。

标签: winapi gdi+


【解决方案1】:

这部分错了:

bminfo1.bmiHeader.biSize = sizeof( BITMAPINFO );

它应该是 sizeof(BITMAPINFOHEADER) 否则它会毁了一切。您也不能使用GetDC(0) 进行任何适当的绘画。改为使用:

HDC hdc = GetDC(hwnd);
...
ReleaseDC(hwnd, hdc);

或使用来自BeginPaintHDC。由于您使用的是 GDI+,那么您必须拥有来自 bmp->GetHBITMAP()HBITMAP 句柄,没有理由转换为内存并返回到 HBITMAP

对于AlphaBlend 设置SourceConstantAlpha = 128;,以防 Alpha 通道未设置。

void blend(HDC hdc, RECT rc, HBITMAP hbitmap1, HBITMAP hbitmap2)
{
    HDC memdc1 = CreateCompatibleDC(hdc);
    HDC memdc2 = CreateCompatibleDC(hdc);

    BITMAP bmp1, bmp2;
    GetObject(hbitmap1, sizeof(BITMAP), &bmp1);
    GetObject(hbitmap2, sizeof(BITMAP), &bmp2);

    SelectObject(memdc1, hbitmap1);
    SelectObject(memdc2, hbitmap2);

    BLENDFUNCTION blend = { 0 };
    blend.SourceConstantAlpha = 128;

    SetStretchBltMode(hdc, COLORONCOLOR);
    AlphaBlend(memdc2, 0, 0, bmp2.bmWidth, bmp2.bmHeight, memdc1, 0, 0, bmp1.bmWidth, bmp1.bmHeight, blend);
    StretchBlt(hdc, 0, 0, rc.right, rc.bottom, memdc2, 0, 0, bmp2.bmWidth, bmp2.bmHeight, SRCCOPY);

    //or create another memdc to get dibs

    DeleteDC(memdc1);
    DeleteDC(memdc2);
}

如果您想获得 dib,则不要使用 hdc,而是创建第三个 memdc 和另一个 HBITMAP,然后使用 GetDIBits

HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbmp = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
SelectObject(memdc, hbmp);
SetStretchBltMode(memdc, COLORONCOLOR);
StretchBlt(memdc, 0, 0, rc.right, rc.bottom, 
    memdc2, 0, 0, bmp2.bmWidth, bmp2.bmHeight, SRCCOPY);

int w = rc.right;
int h = rc.bottom;
BITMAPINFOHEADER bmpInfoHeader = { sizeof(BITMAPINFOHEADER) };
bmpInfoHeader.biWidth = w;
bmpInfoHeader.biHeight = h;
bmpInfoHeader.biBitCount = 32;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biPlanes = 1;

DWORD size = w * 4 * h;
char *dib = new char[size];
GetDIBits(hdc, hbmp, 0, h, dib, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS);
...
DeleteDC(memdc);
DeleteObject(hbitmap);
delete[]dib;

编辑

方法 2:此方法应该更快,因为它使用一个StretchBlt 和一个AlphaBlend。这样您就可以使用预先计算的 alpha,尽管这不是必需的。

仅当您想将两个图像与背景混合时,才使用带有 2 AlphaBlend 的其他方法。

void modify_bits(HDC hdc, HBITMAP hbitmap) 
{   //expecting 32-bit bitmap
    BITMAP bm = { 0 };
    GetObject(hbitmap, sizeof(bm), &bm);
    int w = bm.bmWidth;
    int h = bm.bmHeight;
    BITMAPINFOHEADER bmpInfoHeader = { sizeof(BITMAPINFOHEADER),
        w, h, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
    BYTE* bits = new BYTE[w * h * 4];
    if (GetDIBits(hdc, hbitmap, 0, h, bits, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS)) {
        BYTE* p = bits;
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                p[3] = 128;
                p[0] = p[0] * p[3] / 255;
                p[1] = p[1] * p[3] / 255;
                p[2] = p[2] * p[3] / 255;
                p += 4;
            }
        }
        SetDIBits(hdc, hbitmap, 0, h, bits, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS);
    }
    delete[] bits;
}

void blend2(HDC hdc, RECT rc, HBITMAP hbitmap1, HBITMAP hbitmap2)
{
    int w = rc.right;
    int h = rc.bottom;

    modify_bits(hdc, hbitmap2);

    HDC memdc1 = CreateCompatibleDC(hdc);
    HDC memdc2 = CreateCompatibleDC(hdc);

    BITMAP bmp1, bmp2;
    GetObject(hbitmap1, sizeof(BITMAP), &bmp1);
    GetObject(hbitmap2, sizeof(BITMAP), &bmp2);
    int w1 = bmp1.bmWidth;
    int h1 = bmp1.bmHeight;
    int w2 = bmp2.bmWidth;
    int h2 = bmp2.bmHeight;

    SelectObject(memdc1, hbitmap1);
    SelectObject(memdc2, hbitmap2);

    BLENDFUNCTION blend = { 0 };
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;

    SetStretchBltMode(hdc, COLORONCOLOR);

    //draw first image normally:
    StretchBlt(hdc, 0, 0, w, h, memdc1, 0, 0, w1, h1, SRCCOPY);

    //AlphaBlend the second image:
    AlphaBlend(hdc, 0, 0, w, h, memdc2, 0, 0, w2, h2, blend);

    DeleteDC(memdc1);
    DeleteDC(memdc2);
}

【讨论】:

  • 如果我只将 SourceConstantAlpha 设置为 128,它可以正常工作。但是当我预乘像素并将 blendop 设置为 AC_SRC_OVER 时,它只会显示黑屏。我不确定像素格式是否是问题。
  • 我没有看到任何与像素预乘相关的代码,所以我无法评论。
  • for (int y=0; y
  • 实际上,我认为我的解释有点不对劲。请参阅更新的答案,方法 2。它应该与预先计算的 alpha 通道一起使用。只需确保 Alpha 通道设置为非零值 (pPixel[3])
  • 我已经尝试将 128 设置为 pixel3 但它仍然没有效果,这意味着它在调用 alphablend 时仍然显示黑屏。我不知道 AlphaBlend 真的可以扩展它,我会试试的。
猜你喜欢
  • 2014-05-18
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
  • 2019-03-22
相关资源
最近更新 更多