GDI + Screen Drawing

I have a problem: I need to draw two png files, one on top of the other. When I do this in the usual way, there is a “blinking” effect (the first image crosses out the second in a short period of time). I use the GDI + library, and my WM_PAINT processing is as follows:

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

displayImage:

 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); } } 

Uploading files to variables:

 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"); 

I heard that I have to do the drawing outside the screen. What should it look like?

+5
source share
3 answers

You do not need all this. You can directly use GDI +:

 static Gdiplus::Image *firstImage; static Gdiplus::Image *secondImage; case WM_CREATE: // or WM_INITDIALOG if it 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; } 

However, this code still draws 2 images back with a possible flicker (for example, your source code). Use double buffering in WM_PAINT to make only one BltBlt . Just change to:

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

Regarding the source code:

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

You must change the function declaration to void displayImage(HBITMAP mBmp, HWND mHwnd, HDC hdc) , then you can transfer hdc directly from WM_PAINT

+6
source

First change the displayImage to take the HDC and RECT from the caller, not HWND.

 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); } } 

Then, in the caller, create a compatible DC and bitmap. This is your off-screen space for compositing. Make displayImage calls with this new DC. This will be PNG per frame. Finally, blit the compiled result to the actual DC window at a time.

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

Finally, if you still see a flash of background color, see Pavan Chandak's answer.

+3
source

Please refer to the message "WM_ERASEBKGND".

In fact, two things happen before loading the second image.

  • First, WM_ERASEBKGND is launched to fill the image area with any current Windows background color.
  • WM_PAINT for rendering actions.

The documentation says, to avoid blink / Flickr, provide a default handler for "WM_ERASEBKGND".

Below is the link, go to the "Control that does not flicker" section. You also have an example.

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

0
source

Source: https://habr.com/ru/post/1259066/


All Articles