如何在 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;
这会绘制一些具有任何颜色的文本,并且每次计时器滴答作响时,文本都会变得更加褪色(更加透明),但是绘制文本的位图的黑色背景已被绘制,但我不希望有背景现在,只是文本。
我假设我需要 TransparentBlt
和 AlphaBlend
的组合。
我将如何解决这个问题?
【问题讨论】:
【参考方案1】:解决方案非常简单。将AC_SRC_ALPHA
用于BLENDFUNCTION
的AlphaFormat
成员,为了使其工作,设置缓冲区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 中制作淡入淡出效果?的主要内容,如果未能解决你的问题,请参考以下文章