【问题标题】:Settings transparency on bitmap before using STM_SETIMAGE使用 STM_SETIMAGE 之前在位图上设置透明度
【发布时间】:2017-01-17 07:34:15
【问题描述】:

我有一个静态控件,我通过使用位图将 STM_IMAGE 发送到控件来设置图像。这工作正常,但我希望位图具有透明背景。我有以下代码:

case WM_PAINT:
{
    HBITMAP hbmBmp, hbmMask;
    BITMAP bm;
    PAINTSTRUCT ps;

    HDC hdc = BeginPaint(hWnd, &ps);
    HWND hCtrl = GetDlgItem(hWnd, ID_BUTTONNEWGAME);
    hbmBmp = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_NEWGAMEBUTTONBITMAP));
    GetObject(hbmBmp, sizeof(BITMAP), &bm);
    hbmMask = CreateBitmapMask(hbmBmp, RGB(0, 0, 0));
    HDC hDCMem = CreateCompatibleDC(hdc);
    HDC hDCMem2 = CreateCompatibleDC(hdc);
    HDC hdcResult = CreateCompatibleDC(hdc);

    SelectObject(hDCMem, hbmMask);
    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCAND);

    SelectObject(hDCMem, hbmBmp);
    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCPAINT);

    // At this point I can see the bitmap with a transparent background at (0,0)
    // The following code attempts to copy from hdc into a memory DCand to form a new bitmap 
    //that I use in the STM_SETIMAGE message

    HBITMAP newBitmap = CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight); // create blank bitmap
    SelectObject(hdcResult, newBitmap); // store in memory DC
    BitBlt(hdcResult, 0, 0, bm.bmWidth, bm.bmHeight, hdc, 0, 0, SRCCOPY); // copy from hdc to memory DC

    // This line is just for checking that hdcResult contains the correct data
    // It copies back from the memory DC into hdc to the right of the original bitmap
    // I now see two bitmaps with transparent bitmaps next to each other as I expected
    BitBlt(hdc, bm.bmWidth, 0, bm.bmWidth, bm.bmHeight, hdcResult, 0, 0, SRCCOPY); 
    EndPaint(hWnd, &ps);

    // sending this does not set the bitmap on the static control
    // I do not see anything where the control should be
    SendMessage(hCtrl, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)newBitmap);
}
break;

在下面的代码中使用 SS_BITMAP 样式创建静态控件:

case WM_CREATE:
{
    HWND newGameText = CreateWindowW(
        L"STATIC",
        L"",
        WS_VISIBLE | WS_CHILD | SS_BITMAP | SS_NOTIFY,  // Styles 
        150,         // x position 
        150,         // y position 
        74,        //  width
        24,        //  height
        hWnd,     // Parent window
        (HMENU)ID_BUTTONNEWGAME,
        (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
        NULL);      // Pointer not needed.
}
break;

我需要将位图复制到设备上下文中控件的特定位置(这似乎不合理)还是我在做一些完全不可能/愚蠢的事情?

【问题讨论】:

  • 我知道我还应该在使用后删除设备上下文。
  • 您没有创建具有 (alpha) 透明度的位图。对BitBlt 的前两次调用通过仅写入未被遮罩的像素来创建透明度的错觉。完成后,尚未更改的像素仍然具有其背景颜色。如果您想要透明度,请使用 STM_SETIMAGEIMAGE_ICON。图标支持透明度(甚至可能使用 introduction of PNG support 实现 alpha 透明度)。
  • 谢谢,但我在按钮上看不到任何东西,甚至没有不透明的位图。我将编辑 OP 以使其更清晰。
  • 您的静态控件是否具有SS_BITMAP 样式?顺便说一句,既然它的ID是ID_BUTTONNEWGAME,那真的是静态控件吗?在传递之前从设备上下文中选择newBitmap 可能也是一个好主意。否则它归设备上下文所有。一旦你实施了适当的清理,这将成为一个问题。
  • 是的,该控件是使用 SS_BITMAP 样式创建的。我将创建代码添加到 OP。

标签: c++ winapi bitmap


【解决方案1】:

我认为控制SS_BITMAP 并处理WM_PAINT 消息没有意义。并且发送STM_SETIMAGE(即更改显示的位图)以响应每个绘制请求(WM_PAINT 消息)是相当过分的,不是吗?完成后,您应该删除所有内容(您创建的 DC 和位图以及 STM_SETIMAGE 消息返回的值(之前传递给控件的位图)。

要么将控件设为SS_BITMAP 并使用STM_SETIMAGE,要么将其设为owner-drawn 并处理WM_DRAWITEM 消息。

您可能还想检查TransparentBlt() 函数。

 


 

我在这里添加了一些工作示例的代码。简单易实现,只需要编写很少的代码。所有者绘制的案例确实绘制了具有透明区域的位图。一切都是使用老式 GDI(不是 GDI+)函数完成的。

在本例中,我创建了一个包含三个静态控件的对话框,以不同的方式实现:

  • IDC_IMG1 : SS_BITMAP 样式,图片设置在资源文件中
  • IDC_IMG2 : SS_BITMAP 样式最初没有图像,图像设置带有 STM_SETIMAGE 消息
  • IDC_IMG3 : SS_OWNERDRAW 样式,显示带有透明区域的位图

 

资源文件内容:

IDB_BITMAP1    BITMAP    "Image1.bmp"
.
.
// Dialog Items
CONTROL    IDB_BITMAP1,IDC_IMG1,"Static",SS_BITMAP,7,7,136,126,WS_EX_CLIENTEDGE
CONTROL    "",IDC_IMG2,"Static",SS_BITMAP,154,7,17,15,WS_EX_CLIENTEDGE
CONTROL    "",IDC_IMG3,"Static",SS_OWNERDRAW,301,7,136,123,WS_EX_CLIENTEDGE

源代码:

// Resources
HBITMAP hBitmap = NULL;
int bmpW, bmpH;

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            if (!hBitmap) // Initialize resources if not initialized already
            {
                BITMAP bmp;
                hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
                GetObject(hBitmap, sizeof(bmp), &bmp);
                bmpW = bmp.bmWidth;
                bmpH = bmp.bmHeight;
            }
            // Set bitmap in IDC_IMG2
            SendDlgItemMessage(hDlg, IDC_IMG2, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
            break;
        case WM_DRAWITEM:
            if (wParam==IDC_IMG3) // Draw transparent bitmap in IDC_IMG3
            {
                LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
                HDC hMem = CreateCompatibleDC(lpDIS->hDC);
                HGDIOBJ hOldBmp = SelectObject(hMem, hBitmap);
                TransparentBlt(lpDIS->hDC, 0, 0, bmpW, bmpH, hMem, 0, 0, bmpW, bmpH, RGB(255,255,255));
                SelectObject(hMem, hOldBmp);
                return TRUE;
            }
            break;
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
                EndDialog(hDlg, LOWORD(wParam));
            break;
    }
    return FALSE;
}

结果如下:

【讨论】:

  • 这并不能解决问题。将静态控件的样式设置为包含SS_BITMAP 并处理它的父级WM_PAINT 完全没有问题。 TransparentBlt 试图解决一个不存在的问题。另外,这也不会产生透明位图。
  • 好吧,我不同意你在这里所说的一切。当然,绘制 SS_BITMAP 静态控件是错误的,因为这些控件具有关联的位图(在资源文件中定义或通过 STM_SETIMAGE msg 分配);他们自己画,那么画他们有什么意义呢?并且应该在每个绘画请求上更改图像(STM_SETIMAGE 实际上是做什么的)?不不不!!!而TransparentBlt() 试图解决一个不存在的问题???当然它确实存在,而这正是 OP 所追求的。轻松搞定,只需一个电话!
  • 这与意见无关。问题中代码的目的是动态生成位图,并将其设置为静态控件的图像。 WM_PAINT 处理程序绘制静态控件(它不是作为所有者绘制的控件创建的)。在WM_PAINT 处理程序中设置位图并不理想(WM_INITDIALOG 会更合适)。但是通过适当的资源管理,这不一定是错误的。 TransparentBlt 没有帮助。您仍然假设问题中的代码正在呈现静态控件。它不是,所有可以推断的信息都在那里。
  • 这就是为什么我对你的回答投了-1票。我不知道,为什么有人会投票。它没有解决问题。
  • 哦,是的,确实如此!我实际上已经提出了两种选择。特别是所有者绘制的解决方案很容易实现。另一个更难,因为必须处理工具几乎不支持的 ico 和 pngs。
猜你喜欢
  • 1970-01-01
  • 2013-07-14
  • 2011-01-02
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多