混合 2 个位图

Blend 2 bitmaps

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

目前我能够将 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 中按像素混合位图。任何帮助将不胜感激。

这部分是错误的:

bminfo1.bmiHeader.biSize = sizeof( BITMAPINFO );

应该是sizeof(BITMAPINFOHEADER),不然就完蛋了。此外,您不能使用 GetDC(0) 进行任何适当的绘画。改用:

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

或使用 BeginPaint 中的 HDC。由于您使用的是 GDI+,因此您必须具有来自 bmp->GetHBITMAP()HBITMAP 句柄,没有理由转换为内存并返回 HBITMAP

如果未设置 alpha 通道,AlphaBlend 设置 SourceConstantAlpha = 128;

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;

编辑

方法二:这种方法应该会更快,因为它使用了一个StretchBlt和一个AlphaBlend。这样您就可以使用 pre-computed 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);
}