离线绘图GDI +

时间:2016-10-31 19:40:42

标签: c++ winapi

我有一个问题 - 我需要绘制两个png文件,一个在另一个上。当我按常规方式进行时,会出现“闪烁”效果(第一张图片在很短的时间内透过第二张图片)。我使用GDI +库,我的WM_PAINT处理如下所示:

case WM_PAINT:
{
    PAINTSTRUCT ps; 
    HDC hdc = BeginPaint( hwnd, & ps );
    displayImage(firstImage, hwnd);
    displayImage(secondImage, hwnd);
    EndPaint( hwnd, & ps );
    break;
}

displayImage function:

void displayImage(HBITMAP mBmp, HWND mHwnd)
{
    RECT myRect;
    BITMAP bm;
    HDC screenDC, memDC;
    HBITMAP oldBmp;
    BLENDFUNCTION bf;

    GetObject(mBmp, sizeof(bm), &bm);

    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 0xff;

    bf.AlphaFormat = AC_SRC_ALPHA;

    screenDC = GetDC(mHwnd);
    GetClientRect(mHwnd, &myRect);

    if (mBmp == NULL)
        FillRect(screenDC, &myRect, WHITE_BRUSH);

    else
    {
        memDC = CreateCompatibleDC(screenDC);
        oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
        AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
        SelectObject(memDC, oldBmp);
        DeleteDC(memDC);
        ReleaseDC(mHwnd, screenDC);
    }
}

将文件加载到变量:

HBITMAP mLoadImg(WCHAR *szFilename)
{
   HBITMAP result=NULL;

   Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
   bitmap->GetHBITMAP(NULL, &result);
   delete bitmap;
   return result;
}


firstImage = mLoadImg(L"data\\img\\screen.png");
secondImage = mLoadImg(L"data\\img\\screen2.png");

我听说我应该做一个离屏绘图。应该怎么样?

3 个答案:

答案 0 :(得分:6)

你不需要所有这些。您可以直接使用GDI +:

static Gdiplus::Image *firstImage;
static Gdiplus::Image *secondImage;

case WM_CREATE: // or WM_INITDIALOG if it's dialog
{
    firstImage = new Gdiplus::Image(L"data\\img\\screen.png");
    secondImage = new Gdiplus::Image(L"data\\img\\screen2.png");
    return 0;
}

case WM_PAINT:
{
    PAINTSTRUCT ps = { 0 };
    HDC hdc = BeginPaint(hwnd, &ps);

    Gdiplus::Graphics gr(hdc);
    gr.DrawImage(firstImage, 0, 0);
    gr.DrawImage(secondImage, 0, 0);//<== this will draw transparently

    EndPaint(hwnd, &ps);

    return 0;
}

但是,此代码仍然可以连续绘制2张图像,可能会闪烁(就像您的原始代码一样)。在WM_PAINT中使用双缓冲,以便只完成一个BltBlt。只需更改为:

if (msg == WM_PAINT)
{
    PAINTSTRUCT ps = { 0 };
    HDC hdc = BeginPaint(hwnd, &ps);

    RECT rc;
    GetClientRect(hwnd, &rc);
    HDC memdc = CreateCompatibleDC(hdc);
    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
    HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);

    FillRect(memdc, &rc, WHITE_BRUSH);
    Gdiplus::Graphics gr(memdc);
    gr.DrawImage(firstImage, 0, 0);
    gr.DrawImage(secondImage, 0, 0);

    BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);

    SelectObject(memdc, oldbmp);
    DeleteObject(hbitmap);
    DeleteDC(memdc);

    EndPaint(hwnd, &ps);

    return 0;
}

至于原始代码:

void displayImage(HBITMAP mBmp, HWND mHwnd)
{
HDC hdc = GetDC(mHwnd);
...
}

您应该将函数声明更改为void displayImage(HBITMAP mBmp, HWND mHwnd, HDC hdc),然后您可以直接从hdc

传递WM_PAINT

答案 1 :(得分:3)

首先,更改displayImage以从调用者而不是HWND获取HDC和RECT。

void displayImage(HBITMAP mBmp, HDC hdc, const RECT &myRect)
{
    if (mBmp == NULL)
        FillRect(screenDC, &myRect, WHITE_BRUSH);
    else
    {
        BITMAP bm;
        GetObject(mBmp, sizeof(bm), &bm);

        HDC memDC = CreateCompatibleDC(screenDC);
        HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, mBmp);

        BLENDFUNCTION bf;
        bf.BlendOp = AC_SRC_OVER;
        bf.BlendFlags = 0;
        bf.SourceConstantAlpha = 0xff;
        bf.AlphaFormat = AC_SRC_ALPHA;

        AlphaBlend(hdc, 0, 0, myRect.right, myRect.bottom, memDC, 0, 0, bm.bmWidth, bm.bmHeight, bf);

        SelectObject(memDC, oldBmp);
        DeleteDC(memDC);
    }
}

然后,在调用者中创建兼容的DC和位图。这些是您进行合成的屏幕外空间。使用这个新的DC调用displayImage。这将在屏幕外组成PNG。最后,一次性将组合结果blit到实际窗口DC。

case WM_PAINT:
{
    PAINTSTRUCT ps; 
    HDC hdc = BeginPaint(hwnd, &ps);
    RECT myRect;
    GetClientRect(hwnd, &myRect);

    // Create an off-screen DC for composing the images.
    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP hbmpMem = CreateCompatibleBitmap(hdc, myRect.right, myRect.bottom);
    HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpMem);

    // Compose the images to the offscreen bitmap.
    displayImage(firstImage, hdcMem, myRect);
    displayImage(secondImage, hdcMem, myRect);

    // Blit the resulting composition to the window DC.
    BitBlt(hdc, 0, 0, myRect.right, myRect.bottom,
           hdcMem, 0, 0, SRCCOPY);

    // Clean up the offscreen stuff.
    SelectObject(hdcMem, hbmpOld);
    DeleteObject(hbmpMem);
    DeleteDC(hdcMem);

    EndPaint(hwnd, &ps);
    break;
}

最后,如果您仍然看到背景颜色的闪光,请参阅Pavan Chandaka的回答。

答案 2 :(得分:0)

处理&#34; WM_ERASEBKGND&#34;你自己留言。

实际上在加载第二张图片之前,会发生两件事。

  1. 首先触发WM_ERASEBKGND以使用当前窗口背景颜色填充图像区域。
  2. WM_PAINT以呈现操作。
  3. 文档说要避免blink / Flickr,为&#34; WM_ERASEBKGND&#34;提供默认处理程序。

    以下是链接,转到&#34;没有闪烁的控件&#34;。你也有一个例子。

    https://msdn.microsoft.com/en-us/library/ms969905.aspx