如何在 WINAPI GDI 中制作淡入淡出效果?

Posted

技术标签:

【中文标题】如何在 WINAPI GDI 中制作淡入淡出效果?【英文标题】:How to make a fade effect in WINAPI GDI? 【发布时间】:2021-12-06 03:18:20 【问题描述】:

下面我有一个代码 sn-p 显示我尝试过的内容。

#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;

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

我假设我需要 TransparentBltAlphaBlend 的组合。

我将如何解决这个问题?

【问题讨论】:

【参考方案1】:

解决方案非常简单。将AC_SRC_ALPHA 用于BLENDFUNCTIONAlphaFormat 成员,为了使其工作,设置缓冲区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_PAINT中设置标志:bfn.AlphaFormat = AC_SRC_ALPHA;

【讨论】:

以上是关于如何在 WINAPI GDI 中制作淡入淡出效果?的主要内容,如果未能解决你的问题,请参考以下文章

请问 在Android编辑mp3时,如何设置mp3的淡入淡出效果?

如何仅使用 CSS 创建淡入/淡出效果?

android 怎么做淡入淡出效果

如何在 HTML5 静音按钮中应用淡入淡出效果

如何在 CSS 中转换 jQuery 淡入淡出效果?

jQuery 效果 – 淡入淡出