首页 > 解决方案 > 如何在 WINAPI GDI 中制作淡入淡出效果?

问题描述

下面我有一个代码片段,显示了我尝试过的内容。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>

#define WC_MAIN "MainClass"

#define WC_NUMBER "NumberClass"
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

void InitClasses(void)
{
    WNDCLASS wc;
    memset(&wc, 0, sizeof(wc));
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpfnWndProc = MainWndProc;
    wc.lpszClassName = WC_MAIN;
    RegisterClass(&wc);
    wc.lpfnWndProc = NumberProc;
    wc.lpszClassName = WC_NUMBER;
    RegisterClass(&wc);
}

#define NUMBER_SPEED 2
#define NUMBER_TICK_SPEED 25
#define NUMBER_TICKS 55

typedef struct {
    UINT ticks;
    HBITMAP buffer;
} NUMBERINFO;

HWND CreateNumber(HWND parent, const char *text, int x, int y, COLORREF color)
{
    NUMBERINFO *ni = malloc(sizeof(*ni));
    HDC hdc = GetDC(NULL);
    HFONT oldFont = NULL;
    SIZE s;
    GetTextExtentPoint32(hdc, text, strlen(text), &s);
    SelectObject(hdc, oldFont);

    BITMAPINFO bmi;
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = s.cx;
    bmi.bmiHeader.biHeight = s.cy;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;         // four 8-bit components
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biSizeImage = s.cx * s.cy * 4;
    COLORREF *pvBits;
    HBITMAP buffer = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &pvBits, NULL, 0);
    HDC bufferDc = CreateCompatibleDC(hdc);
    HBITMAP oldBmp = SelectObject(bufferDc, buffer);
    oldFont = NULL;

    SetTextAlign(bufferDc, TA_TOP | TA_LEFT);
    SetBkMode(bufferDc, TRANSPARENT);
    SetTextColor(bufferDc, color);

    TextOut(bufferDc, 0, 0, text, strlen(text));

    SelectObject(bufferDc, oldFont);
    SelectObject(bufferDc, oldBmp);
    DeleteDC(bufferDc);

    ReleaseDC(NULL, hdc);
    ni->buffer = buffer;
    return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx >> 1), y - (s.cy >> 1), s.cx, s.cy, parent, NULL, NULL, ni);
}

LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    NUMBERINFO *info = (NUMBERINFO*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
    switch(msg)
    {
    case WM_CREATE:
        info = ((CREATESTRUCT*) lParam)->lpCreateParams;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) info);
        SetTimer(hWnd, 0, NUMBER_TICK_SPEED, NULL);
        info->ticks = NUMBER_TICKS;
        return 0;
    case WM_DESTROY: return 0;
    case WM_TIMER:
    {
        RECT rc;
        GetWindowRect(hWnd, &rc);
        HWND parent = GetParent(hWnd);
        MapWindowPoints(HWND_DESKTOP, parent, (POINT*) &rc, 2);
        rc.top -= NUMBER_SPEED;
        if(!--info->ticks)
        {
            DestroyWindow(hWnd);
            return 0;
        }
        SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top - NUMBER_SPEED, SWP_NOREDRAW | SWP_NOCOPYBITS);
        // redraw parent call erases last shown number
        RedrawWindow(GetParent(hWnd), &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
        return 0;
    }
    case WM_ERASEBKGND: return 1;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        RECT r;
        GetClientRect(hWnd, &r);
        HDC bufferDc = CreateCompatibleDC(hdc);
        HBITMAP oldBmp = SelectObject(bufferDc, info->buffer);

        BLENDFUNCTION bfn;
        bfn.BlendOp = AC_SRC_OVER;
        bfn.BlendFlags = 0;
        bfn.SourceConstantAlpha = info->ticks * 0xFF / NUMBER_TICKS;
        bfn.AlphaFormat = 0;
        //TransparentBlt(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, 0);
        AlphaBlend(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, bfn);

        SelectObject(bufferDc, oldBmp);
        DeleteDC(bufferDc);
        EndPaint(hWnd, &ps);
        return 0;
    }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_DESTROY: PostQuitMessage(0); return 0;
    case WM_ERASEBKGND: return 1;
    case WM_PAINT:
    {
        InvalidateRect(hWnd, NULL, FALSE);
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // draw gradient (red to green)
        RECT r;
        GetClientRect(hWnd, &r);
        GRADIENT_RECT gr;
        gr.UpperLeft = 0;
        gr.LowerRight = 1;
        TRIVERTEX pVertex[2] = {
            { 0,              0, 0xFF00, 0x0000, 0x0000, 0xFF00 },
            { r.right, r.bottom, 0x0000, 0xFF00, 0x0000, 0xFF00 }
        };
        GradientFill(hdc, pVertex, 2, &gr, 2, GRADIENT_FILL_RECT_H);
        EndPaint(hWnd, &ps);
        return 0;
    }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}



int main()
{
    InitClasses();
    HWND mainWindow = CreateWindow(WC_MAIN, "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);

    ShowWindow(mainWindow, 1);
    UpdateWindow(mainWindow);
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        // adding a number at mouse position if pressed (testing)
        if(msg.message == WM_LBUTTONDOWN)
        {
            RECT r;
            GetClientRect(mainWindow, &r);
            int x = GET_X_LPARAM(msg.lParam);
            int y = GET_Y_LPARAM(msg.lParam);
            CreateNumber(mainWindow, "123", x, y, 0xFFFF00);
        }
    }
    return 0;
}

这会绘制一些具有任何颜色的文本,并且每次计时器滴答作响时,文本都会变得更加褪色(更加透明),但是绘制文本的位图的黑色背景已被绘制,但我希望没有背景存在,只是文本。

我会假设我需要TransparentBlt和的组合AlphaBlend

我将如何继续解决这个问题?

标签: cwinapi

解决方案


解决方案非常简单。它是AC_SRC_ALPHA用于AlphaFormat成员的,BLENDFUNCTION并且为了使其工作,设置HBITMAP缓冲区的 alpha 值。

在创建缓冲区(内部的那个NUMBERINFO)之后(在CreateNumber;检查问题代码以供参考),必须循环所有颜色并在需要时将 alpha 值设置为 255。

for(int i = s.cx * s.cy; i--; pvBits++)
{
    if(*pvBits)
        *pvBits |= 0xFF000000; // make non black pixels opaque
}

并将标志设置为WM_PAINTbfn.AlphaFormat = AC_SRC_ALPHA;


推荐阅读