最终的目标是在窗户中使用透明屏幕,但这不是我现在所困扰的。
为了创建透明窗口,我首先尝试使用GDI +将闪屏和文本合成在屏幕外缓冲区上。
目前,我只是尝试合成缓冲区并显示它以响应“WM_PAINT”消息。目前还没有结果;我看到的只是一扇黑色的窗户。
我想我已经误解了在GDI +中设置渲染目标然后渲染它们(我正试图使用直接GDI blit渲染屏幕)
无论如何,这是迄今为止的代码:
//my window initialisation code
void MyWindow::create_hwnd(HINSTANCE instance, const SIZE &dim)
{
DWORD ex_style = WS_EX_LAYERED ; //eventually I'll be making use of this layerd flag
m_hwnd = CreateWindowEx(
ex_style,
szFloatingWindowClass ,
L"",
WS_POPUP ,
0,
0,
dim.cx,
dim.cy,
null,
null,
instance,
null);
m_display_dc = GetDC(NULL);
//This was sanity check test code - just loading a standard HBITMAP and displaying it in WM_PAINT. It worked fine
//HANDLE handle= LoadImage(NULL , L"c:\\test_image2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_gdip_offscreen_bm = new Gdiplus::Bitmap(dim.cx, dim.cy);
m_gdi_dc = Gdiplus::Graphics::FromImage(m_gdip_offscreen_bm);//new Gdiplus::Graphics(m_splash_dc );//window_dc ;m_splash_dc
//this draws the conents of my splash screen - this works if I create a GDI+ context for the window, rather than for an offscreen bitmap.
//For all I know, it might actually be working but when I try to display the contents on screen, it shows a black image
draw_all();
//this is just to show that drawing something simple on the offscreen bit map seems to have no effect
Gdiplus::Pen pen(Gdiplus::Color(255, 0, 0, 255));
m_gdi_dc->DrawLine(&pen, 0,0,100,100);
DWORD last_error = GetLastError(); //returns '0' at this stage
}
这是处理WM_PAINT消息的片段:
---8<-----------------------
//Paint message snippit
case WM_PAINT:
{
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(vg->m_hwnd, &ps); //get the HWNDs DC
HDC hdcMem = vg->m_gdi_dc->GetHDC(); //get the HDC from our offscreen GDI+ object
unsigned int width = vg->m_gdip_offscreen_bm->GetWidth(); //width and height seem fine at this point
unsigned int height = vg->m_gdip_offscreen_bm->GetHeight();
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY); //this blits a black rectangle
DWORD last_error = GetLastError(); //this was '0'
vg->m_gdi_dc->ReleaseHDC(hdcMem);
EndPaint(vg->m_hwnd, &ps); //end paint
return 1;
}
---8<-----------------------
我为长篇大论道歉。有没有人知道我对你如何使用GDI +(或GDI)写一个屏幕外缓冲区然后在屏幕上显示这个问题并不太了解?
感谢您的阅读。
答案 0 :(得分:2)
解决了它,虽然我确定这不是最佳的。 首先,正如Chris Becke建议的那样,您不应该响应WM_PAINT消息,而只应使用'UpdateLayeredWindow'进行更新。我遇到的一个问题是给UpdateLayeredWindow一些不好的坐标,特别是我交换了源和目标(doh!)
这是从GDI +表面调用UpdateLayeredWindow的代码。它不是最佳的,但它有效。
void TOPLEVEL_FLOATING_WINDOW::update_layered_window()
{
BLENDFUNCTION blend_func = { 0 }; //build the blend function object... I guess microsoft doesn't/didn't like constructors
blend_func.AlphaFormat = AC_SRC_OVER;
blend_func.BlendFlags = 0;
blend_func.SourceConstantAlpha = 255;
blend_func.AlphaFormat = AC_SRC_ALPHA;
POINT_2i pt = m_screen_pos;
VECTOR_2i dim = m_bounds.dimensions();
POINT_2i pt_src (0,0) ;
Gdiplus::Color bg(0,0,0,0);
m_gdip_offscreen_bm->GetHBITMAP(bg, &m_offscreen_bm); //get the Hbitmap from my GDI+ bitmap - I think this is allocating a whole new bitmap off the heap. Yuck, very inefficient!
HDC splash_dc = CreateCompatibleDC(m_display_dc); //create a temporary HDC
HGDIOBJ oldobj = SelectObject(splash_dc , m_offscreen_bm); //'select' the bitmap.
UpdateLayeredWindow(m_hwnd,m_display_dc, (POINT*)&pt, (SIZE*)&dim, splash_dc , (POINT*)&pt_src, 0 ,&blend_func,ULW_ALPHA); //this call works and updates our splash screens hidden buffer
SelectObject(splash_dc, oldobj); //some tidy up code
DeleteObject(m_offscreen_bm); //free the bitmap. Memory fragmentation HO!
DeleteDC(splash_dc); //Delete the DC
}
这就是我认为应该起作用但不起作用的东西。我会向任何能告诉我原因的人发一些意见!我想这是因为HDC并非天生相同,而且UpdateLayeredWindow函数只能以某种方式接受某个源创建的HDC。如果这个规则更加明显,那就太好了。
void TOPLEVEL_FLOATING_WINDOW::update_layered_window_in_an_ideal_world()
{
m_refresh_needed = false;
BLENDFUNCTION blend_func = { 0 };
blend_func.AlphaFormat = AC_SRC_OVER;
blend_func.BlendFlags = 0;
blend_func.SourceConstantAlpha = 255;
blend_func.AlphaFormat = AC_SRC_ALPHA;
POINT_2i pt = m_screen_pos;
VECTOR_2i dim = m_bounds.dimensions();
POINT_2i pt_src (0,0) ;
HDC gdi_HDC = m_screen_gdi_dc->GetHDC(); //I have a GDI+ 'Graphics' object whose back buffer is the image I want to composite on to the desktop
UpdateLayeredWindow(m_hwnd,m_display_dc, (POINT*)&pt, (SIZE*)&dim, gdi_HDC , (POINT*)&pt_src, 0 ,&blend_func,ULW_ALPHA);
m_screen_gdi_dc->ReleaseHDC(gdi_HDC); //be nice and release the gdi_HDC
}
唯一的另一种选择是忽略GDI +并使用我自己的光栅库渲染所有内容,非常乐意渲染到任何屏幕外缓冲区。但是,这个项目有点过分。
答案 1 :(得分:2)
这是一种将GDI +位图转换为分层窗口的方法。这允许多个位图叠加,定位和重新调整大小。
void SetSplashImage()
{
// Default to upper left of screen
POINT ptOrigin = { 0, 0 };
SIZE sizeSplash = { 128, 128 };
// Get the actual screen location
GetPointOfOrigin(ptOrigin, sizeSplash);
// Our in memory database of GDI+ Bitmaps
data::image::BoxOfBits *box =
dynamic_cast<data::image::BoxOfBits *>(&Images.get_package("skin_layout_008"));
// Create a display context as a canvas to draw the images
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP bmMem = CreateCompatibleBitmap(hdcScreen, sizeSplash.cx, sizeSplash.cy);
// Prep canvas for rendering graphic
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, bmMem);
Gdiplus::Graphics graphics(hdcMem);
// Base image is a pic of a folder
Gdiplus::RectF canvasDest(0,0,128,128);
graphics.DrawImage(box->grab("black_folder").pBitmap_,
canvasDest, 0,0,128,128, Gdiplus::UnitPixel);
// Overlay a pic of some tools in center of folder
Gdiplus::RectF canvasDest2(30,50,64,64); // resize half actual size
graphics.DrawImage(box->grab("work_tools").pBitmap_,
canvasDest2, 0,0,128,128, Gdiplus::UnitPixel);
// Overlay a pic of a cog in upper left corner of folder
Gdiplus::RectF canvasDest1(16,16,32,32); // resize half actual size
graphics.DrawImage(box->grab("cog_edit").pBitmap_,
canvasDest1, 0,0,32,32, Gdiplus::UnitPixel);
// Prepare to alpha blend the canvas with the screen
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// Composite the canvas with the screen into the layered window
POINT ptZero = { 0 };
UpdateLayeredWindow(hwnd_, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// Delete temporary objects used for canvas
SelectObject(hdcMem, hbmpOld);
DeleteObject(bmMem);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
// Update mouse hit-test coordinates
GetWindowRect(hwnd_, &rcMousedown_);
}
由三个位图组成的分层窗口在屏幕上显示如下: - 猜我没有足够的声望发布图片:(