如何在内存中的位图上绘制文本[无MFC]

时间:2018-02-09 13:03:58

标签: c++ c winapi

有人可以解释如何在内存中的位图上绘制文本吗? 我有以下代码,但我无法弄清楚如何做到这一点。

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    HDC buffDC = CreateCompatibleDC(hdc);

    SelectObject(buffDC, hFnt);
    SetTextColor(buffDC, RGB(1, 1, 1));
    SetBkColor(buffDC, RGB(0, 255, 0));

    RECT rc;
    GetClientRect(hWnd, &rc);
    HBITMAP buffBitmap = CreateCompatibleBitmap(buffDC, rc.right, rc.bottom);

    int savedDC = SaveDC(buffDC);

    HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
    FillRect(buffDC, &rc, hBrush);
    DeleteObject(hBrush);

    //This is the part where i would like to draw to the bitmap
    TextOutA(buffDC, 0, 0, "Hello", 6);

    SelectObject(buffDC, buffBitmap);
    BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);
    RestoreDC(buffDC, savedDC);


    DeleteObject(buffBitmap);
    DeleteDC(buffDC);
    EndPaint(hWnd, &ps);
    break;
}

我见过许多不同的解决方案,主要是使用MFC,但是如果可能的话我想避免这种方法。

编辑:我已经检查了其他已经问过的问题,但是如果没有MFC,我找不到一个会覆盖这个问题。

我最初的问题是我正在使用计时器调用RedrawWindow并更新文本的位置并制作一种从右向左移动的滚动文本。

当我在测试过程中时,我注意到在某些机器上,应用程序以高达25%的CPU使用率运行,而在其他一些机器上,它使用< 1%。我已经使用运行Windows 7的完全相同的规格对应用程序测试了两台机器,并且应用程序运行了一个~10%,另一个运行0%。

(顺便说一句,我的计时器每33毫秒调用一次,RedrawWindow使用RDW_UPDATENOW,我也没有处理WM_ERASEBKGND消息:P 由于WM_TIMER(据我所知)是一个低优先级的消息,我不关心定时器导致CPU使用问题。)

我认为也许我应该使用位图和BitBlt它到屏幕而不是简单地绘制到dc并每次重新绘制屏幕时更新x坐标。

由于

2 个答案:

答案 0 :(得分:2)

在绘制位图之前,必须将其选择到内存设备上下文中。

在第一次调用绘图函数之前移动SelectObject(buffDC, buffBitmap);,但通常在创建位图后尽快。

在您的示例代码中,似乎适合在SaveDC()调用后插入它,以便稍后在您致电RestoreDC()时恢复原始位图:

int savedDC = SaveDC(buffDC);
SelectObject(buffDC, buffBitmap);

评论者指出,CreateCompatibleBitmap(buffDC, rc.right, rc.bottom)应更改为CreateCompatibleBitmap(hdc, rc.right, rc.bottom)。 来自CreateCompatibleBitmap()的参考:

  

创建内存设备上下文时,它最初为1 x 1   选择单色位图。如果这个内存设备上下文是   在CreateCompatibleBitmap中使用,创建的位图是   单色位图。要创建颜色位图,请使用所使用的HDC   创建内存设备上下文

最后一个建议:如果您只需要一个临时位图(如示例代码中所示),则自Windows Vista以来可以使用更高效的API。它被称为缓冲绘制API 。 MSDN似乎没有提供良好的概述,这里是tutorialreference(所有在其名称中都有“BufferedPaint”的函数)。

答案 1 :(得分:0)

以下是适用于我的Window程序,它基于zett42的答案。 这段代码仅用于测试目的,因为我无法发布因工作而正在处理的应用程序的原始源代码。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   static int xPos;
   const bool bIsBufferedPaint = true;
   switch (message)
   {
   case WM_CREATE:
   {
      BufferedPaintInit();
   }
   break;
   case WM_PAINT:
      {
         PAINTSTRUCT ps;
         HDC hdc = BeginPaint(hWnd, &ps);
         if(bIsBufferedPaint)
         {
            HDC newDC;
            RECT rc;
            RECT dstrc;
            GetClientRect(hWnd, &rc);
            dstrc = rc;
            dstrc.left = rc.right + xPos;
            HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(hdc, &rc, BPBF_COMPATIBLEBITMAP, NULL, &newDC);
            if(hBufferedPaint)
            {
                BufferedPaintClear(hBufferedPaint, NULL);
                SetTextColor(newDC, RGB(0, 0, 0));
                DrawText(newDC, L"Using buffered paint", -1, &dstrc, DT_SINGLELINE | DT_VCENTER | DT_LEFT);
                Sleep(2);
                EndBufferedPaint(hBufferedPaint, TRUE);

            }
            else
            {
            // buffer paint did not work.
            }
        }
        else
        {
            HDC buffDC = CreateCompatibleDC(hdc);
            SetTextColor(buffDC, RGB(0, 0, 0));
            SetBkColor(buffDC, RGB(255, 255, 255));

            RECT rc;
            GetClientRect(hWnd, &rc);
            HBITMAP buffBitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);

            int savedDC = SaveDC(buffDC);
            SelectObject(buffDC, buffBitmap);

            HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
            FillRect(buffDC, &rc, hBrush);
            DeleteObject(hBrush);
            std::string testText = "Not using the buffered paint API";
            TextOutA(buffDC, xPos, 0, testText.c_str(), testText.size());
            BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);

            RestoreDC(buffDC, savedDC);

            DeleteObject(buffBitmap);
            DeleteDC(buffDC);
         }

        EndPaint(hWnd, &ps);
        }
        break;
   case WM_ERASEBKGND:
       return 1;
    case WM_TIMER:
        {
        switch(wParam)
        {
        case TIMER1:
            {
                xPos--;
                RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASE);
                if(bIsBufferedPaint)
                {
                    if(xPos <= -500)
                        xPos = 0;
                }
                else
                {
                    if(xPos <= -50)
                        xPos = 1000;
                }

            }
            break;
        }
        }
        break;
    case WM_NCDESTROY:
    {
         BufferedPaintUnInit();
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:

        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}