Как создать эффект затухания в WINAPI GDI?

#c #winapi

Вопрос:

Ниже у меня есть фрагмент кода, показывающий, что я пробовал.

 #include lt;stdio.hgt; #include lt;stdlib.hgt; #include lt;windows.hgt; #include lt;windowsx.hgt;  #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(amp;wc, 0, sizeof(wc));  wc.hbrBackground = GetStockObject(WHITE_BRUSH);  wc.hCursor = LoadCursor(NULL, IDC_ARROW);  wc.lpfnWndProc = MainWndProc;  wc.lpszClassName = WC_MAIN;  RegisterClass(amp;wc);  wc.lpfnWndProc = NumberProc;  wc.lpszClassName = WC_NUMBER;  RegisterClass(amp;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), amp;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, amp;bmi, DIB_RGB_COLORS, (void**) amp;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-gt;buffer = buffer;  return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx gt;gt; 1), y - (s.cy gt;gt; 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)-gt;lpCreateParams;  SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) info);  SetTimer(hWnd, 0, NUMBER_TICK_SPEED, NULL);  info-gt;ticks = NUMBER_TICKS;  return 0;  case WM_DESTROY: return 0;  case WM_TIMER:  {  RECT rc;  GetWindowRect(hWnd, amp;rc);  HWND parent = GetParent(hWnd);  MapWindowPoints(HWND_DESKTOP, parent, (POINT*) amp;rc, 2);  rc.top -= NUMBER_SPEED;  if(!--info-gt;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), amp;rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);  return 0;  }  case WM_ERASEBKGND: return 1;  case WM_PAINT:  {  PAINTSTRUCT ps;  HDC hdc = BeginPaint(hWnd, amp;ps);  RECT r;  GetClientRect(hWnd, amp;r);  HDC bufferDc = CreateCompatibleDC(hdc);  HBITMAP oldBmp = SelectObject(bufferDc, info-gt;buffer);   BLENDFUNCTION bfn;  bfn.BlendOp = AC_SRC_OVER;  bfn.BlendFlags = 0;  bfn.SourceConstantAlpha = info-gt;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, amp;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, amp;ps);  // draw gradient (red to green)  RECT r;  GetClientRect(hWnd, amp;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, amp;gr, 2, GRADIENT_FILL_RECT_H);  EndPaint(hWnd, amp;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(amp;msg, NULL, 0, 0))  {  TranslateMessage(amp;msg);  DispatchMessage(amp;msg);  // adding a number at mouse position if pressed (testing)  if(msg.message == WM_LBUTTONDOWN)  {  RECT r;  GetClientRect(mainWindow, amp;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 для AlphaFormat члена BLENDFUNCTION и для того, чтобы это работало , установите альфа-значения HBITMAP буфера.

После того, как буфер (тот, что внутри NUMBERINFO ) создан (в CreateNumber ; проверьте код вопроса для справки), необходимо перебрать все цвета и при необходимости установить альфа-значение 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;